diff --git a/.github/workflows/unit-tests-internal.yml b/.github/workflows/unit-tests-internal.yml new file mode 100644 index 0000000000..38aba1cc95 --- /dev/null +++ b/.github/workflows/unit-tests-internal.yml @@ -0,0 +1,130 @@ +name: "๐Ÿงช Unit Tests: Internal" + +permissions: + contents: read + +on: + workflow_call: + +jobs: + unitTests: + name: "๐Ÿงช Unit Tests: Internal" + runs-on: ubuntu-latest + strategy: + matrix: + shardIndex: [1, 2, 3, 4, 5, 6, 7, 8] + shardTotal: [8] + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + SHARD_INDEX: ${{ matrix.shardIndex }} + SHARD_TOTAL: ${{ matrix.shardTotal }} + steps: + - name: ๐Ÿ”ง Disable IPv6 + run: | + sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 + + - name: ๐Ÿ”ง Configure docker address pool + run: | + CONFIG='{ + "default-address-pools" : [ + { + "base" : "172.17.0.0/12", + "size" : 20 + }, + { + "base" : "192.168.0.0/16", + "size" : 24 + } + ] + }' + mkdir -p /etc/docker + echo "$CONFIG" | sudo tee /etc/docker/daemon.json + + - name: ๐Ÿ”ง Restart docker daemon + run: sudo systemctl restart docker + + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + cache: "pnpm" + + # ..to avoid rate limits when pulling images + - name: ๐Ÿณ Login to DockerHub + if: ${{ env.DOCKERHUB_USERNAME }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: ๐Ÿณ Skipping DockerHub login (no secrets available) + if: ${{ !env.DOCKERHUB_USERNAME }} + run: echo "DockerHub login skipped because secrets are not available." + + - name: ๐Ÿ“ฅ Download deps + run: pnpm install --frozen-lockfile + + - name: ๐Ÿ“€ Generate Prisma Client + run: pnpm run generate + + - name: ๐Ÿงช Run Internal Unit Tests + run: pnpm run test:internal --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + + - name: Gather all reports + if: ${{ !cancelled() }} + run: | + mkdir -p .vitest-reports + find . -type f -path '*/.vitest-reports/blob-*.json' \ + -exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \; + + - name: Upload blob reports to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: internal-blob-report-${{ matrix.shardIndex }} + path: .vitest-reports/* + include-hidden-files: true + retention-days: 1 + + merge-reports: + name: "๐Ÿ“Š Merge Reports" + if: ${{ !cancelled() }} + needs: [unitTests] + runs-on: ubuntu-latest + steps: + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + # no cache enabled, we're not installing deps + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v4 + with: + path: .vitest-reports + pattern: internal-blob-report-* + merge-multiple: true + + - name: Merge reports + run: pnpm dlx vitest run --merge-reports diff --git a/.github/workflows/unit-tests-packages.yml b/.github/workflows/unit-tests-packages.yml new file mode 100644 index 0000000000..0eb2ba0710 --- /dev/null +++ b/.github/workflows/unit-tests-packages.yml @@ -0,0 +1,130 @@ +name: "๐Ÿงช Unit Tests: Packages" + +permissions: + contents: read + +on: + workflow_call: + +jobs: + unitTests: + name: "๐Ÿงช Unit Tests: Packages" + runs-on: ubuntu-latest + strategy: + matrix: + shardIndex: [1] + shardTotal: [1] + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + SHARD_INDEX: ${{ matrix.shardIndex }} + SHARD_TOTAL: ${{ matrix.shardTotal }} + steps: + - name: ๐Ÿ”ง Disable IPv6 + run: | + sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 + + - name: ๐Ÿ”ง Configure docker address pool + run: | + CONFIG='{ + "default-address-pools" : [ + { + "base" : "172.17.0.0/12", + "size" : 20 + }, + { + "base" : "192.168.0.0/16", + "size" : 24 + } + ] + }' + mkdir -p /etc/docker + echo "$CONFIG" | sudo tee /etc/docker/daemon.json + + - name: ๐Ÿ”ง Restart docker daemon + run: sudo systemctl restart docker + + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + cache: "pnpm" + + # ..to avoid rate limits when pulling images + - name: ๐Ÿณ Login to DockerHub + if: ${{ env.DOCKERHUB_USERNAME }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: ๐Ÿณ Skipping DockerHub login (no secrets available) + if: ${{ !env.DOCKERHUB_USERNAME }} + run: echo "DockerHub login skipped because secrets are not available." + + - name: ๐Ÿ“ฅ Download deps + run: pnpm install --frozen-lockfile + + - name: ๐Ÿ“€ Generate Prisma Client + run: pnpm run generate + + - name: ๐Ÿงช Run Package Unit Tests + run: pnpm run test:packages --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + + - name: Gather all reports + if: ${{ !cancelled() }} + run: | + mkdir -p .vitest-reports + find . -type f -path '*/.vitest-reports/blob-*.json' \ + -exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \; + + - name: Upload blob reports to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: packages-blob-report-${{ matrix.shardIndex }} + path: .vitest-reports/* + include-hidden-files: true + retention-days: 1 + + merge-reports: + name: "๐Ÿ“Š Merge Reports" + if: ${{ !cancelled() }} + needs: [unitTests] + runs-on: ubuntu-latest + steps: + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + # no cache enabled, we're not installing deps + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v4 + with: + path: .vitest-reports + pattern: packages-blob-report-* + merge-multiple: true + + - name: Merge reports + run: pnpm dlx vitest run --merge-reports diff --git a/.github/workflows/unit-tests-webapp.yml b/.github/workflows/unit-tests-webapp.yml new file mode 100644 index 0000000000..cccb8fbcfc --- /dev/null +++ b/.github/workflows/unit-tests-webapp.yml @@ -0,0 +1,136 @@ +name: "๐Ÿงช Unit Tests: Webapp" + +permissions: + contents: read + +on: + workflow_call: + +jobs: + unitTests: + name: "๐Ÿงช Unit Tests: Webapp" + runs-on: ubuntu-latest + strategy: + matrix: + shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shardTotal: [10] + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + SHARD_INDEX: ${{ matrix.shardIndex }} + SHARD_TOTAL: ${{ matrix.shardTotal }} + steps: + - name: ๐Ÿ”ง Disable IPv6 + run: | + sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 + + - name: ๐Ÿ”ง Configure docker address pool + run: | + CONFIG='{ + "default-address-pools" : [ + { + "base" : "172.17.0.0/12", + "size" : 20 + }, + { + "base" : "192.168.0.0/16", + "size" : 24 + } + ] + }' + mkdir -p /etc/docker + echo "$CONFIG" | sudo tee /etc/docker/daemon.json + + - name: ๐Ÿ”ง Restart docker daemon + run: sudo systemctl restart docker + + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + cache: "pnpm" + + # ..to avoid rate limits when pulling images + - name: ๐Ÿณ Login to DockerHub + if: ${{ env.DOCKERHUB_USERNAME }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: ๐Ÿณ Skipping DockerHub login (no secrets available) + if: ${{ !env.DOCKERHUB_USERNAME }} + run: echo "DockerHub login skipped because secrets are not available." + + - name: ๐Ÿ“ฅ Download deps + run: pnpm install --frozen-lockfile + + - name: ๐Ÿ“€ Generate Prisma Client + run: pnpm run generate + + - name: ๐Ÿงช Run Webapp Unit Tests + run: pnpm run test:webapp --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + env: + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres + DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres + SESSION_SECRET: "secret" + MAGIC_LINK_SECRET: "secret" + ENCRYPTION_KEY: "secret" + + - name: Gather all reports + if: ${{ !cancelled() }} + run: | + mkdir -p .vitest-reports + find . -type f -path '*/.vitest-reports/blob-*.json' \ + -exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \; + + - name: Upload blob reports to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: webapp-blob-report-${{ matrix.shardIndex }} + path: .vitest-reports/* + include-hidden-files: true + retention-days: 1 + + merge-reports: + name: "๐Ÿ“Š Merge Reports" + if: ${{ !cancelled() }} + needs: [unitTests] + runs-on: ubuntu-latest + steps: + - name: โฌ‡๏ธ Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: โŽ” Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.5 + + - name: โŽ” Setup node + uses: buildjet/setup-node@v4 + with: + node-version: 20.11.1 + # no cache enabled, we're not installing deps + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v4 + with: + path: .vitest-reports + pattern: webapp-blob-report-* + merge-multiple: true + + - name: Merge reports + run: pnpm dlx vitest run --merge-reports diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 74a9f4af12..7c90a5a30a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -7,82 +7,12 @@ on: workflow_call: jobs: - unitTests: - name: "๐Ÿงช Unit Tests" - runs-on: ubuntu-latest - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} - steps: - - name: ๐Ÿ”ง Disable IPv6 - run: | - sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 - sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 - sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 - - - name: ๐Ÿ”ง Configure docker address pool - run: | - CONFIG='{ - "default-address-pools" : [ - { - "base" : "172.17.0.0/12", - "size" : 20 - }, - { - "base" : "192.168.0.0/16", - "size" : 24 - } - ] - }' - mkdir -p /etc/docker - echo "$CONFIG" | sudo tee /etc/docker/daemon.json - - - name: ๐Ÿ”ง Restart docker daemon - run: sudo systemctl restart docker - - - name: โฌ‡๏ธ Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: โŽ” Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8.15.5 - - - name: โŽ” Setup node - uses: buildjet/setup-node@v4 - with: - node-version: 20.11.1 - cache: "pnpm" - - # ..to avoid rate limits when pulling images - - name: ๐Ÿณ Login to DockerHub - if: ${{ env.DOCKERHUB_USERNAME }} - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: ๐Ÿณ Skipping DockerHub login (no secrets available) - if: ${{ !env.DOCKERHUB_USERNAME }} - run: echo "DockerHub login skipped because secrets are not available." - - - name: ๐Ÿ“ฅ Download deps - run: pnpm install --frozen-lockfile - - - name: ๐Ÿ“€ Generate Prisma Client - run: pnpm run generate - - - name: ๐Ÿงช Run Webapp Unit Tests - run: pnpm run test:webapp - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres - DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres - SESSION_SECRET: "secret" - MAGIC_LINK_SECRET: "secret" - ENCRYPTION_KEY: "secret" - - - name: ๐Ÿงช Run Package Unit Tests - run: pnpm run test:packages - - - name: ๐Ÿงช Run Internal Unit Tests - run: pnpm run test:internal + webapp: + uses: ./.github/workflows/unit-tests-webapp.yml + secrets: inherit + packages: + uses: ./.github/workflows/unit-tests-packages.yml + secrets: inherit + internal: + uses: ./.github/workflows/unit-tests-internal.yml + secrets: inherit diff --git a/apps/supervisor/package.json b/apps/supervisor/package.json index 7d0c148cb3..82278b8f01 100644 --- a/apps/supervisor/package.json +++ b/apps/supervisor/package.json @@ -22,7 +22,6 @@ "zod": "3.23.8" }, "devDependencies": { - "@types/dockerode": "^3.3.33", - "vitest": "^1.4.0" + "@types/dockerode": "^3.3.33" } } diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 95c843c0eb..99867164e3 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -254,8 +254,7 @@ "tailwindcss": "3.4.1", "ts-node": "^10.7.0", "tsconfig-paths": "^3.14.1", - "vite-tsconfig-paths": "^4.0.5", - "vitest": "^1.4.0" + "vite-tsconfig-paths": "^4.0.5" }, "engines": { "node": ">=16.0.0" diff --git a/apps/webapp/test/fairDequeuingStrategy.test.ts b/apps/webapp/test/fairDequeuingStrategy.test.ts index 63ccf3fc16..2202576c9d 100644 --- a/apps/webapp/test/fairDequeuingStrategy.test.ts +++ b/apps/webapp/test/fairDequeuingStrategy.test.ts @@ -256,12 +256,15 @@ describe("FairDequeuingStrategy", () => { "consumer-1" ); + const tolerance = 0.15; + const withTolerance = (value: number) => value * (1 + tolerance); + const distribute2Duration = performance.now() - startDistribute2; console.log("Second distribution took", distribute2Duration, "ms"); // Make sure the second call is more than 9 times faster than the first - expect(distribute2Duration).toBeLessThan(distribute1Duration / 9); + expect(distribute2Duration).toBeLessThan(withTolerance(distribute1Duration / 9)); const startDistribute3 = performance.now(); @@ -275,7 +278,7 @@ describe("FairDequeuingStrategy", () => { console.log("Third distribution took", distribute3Duration, "ms"); // Make sure the third call is more than 4 times the second - expect(distribute3Duration).toBeGreaterThan(distribute2Duration * 4); + expect(withTolerance(distribute3Duration)).toBeGreaterThan(distribute2Duration * 4); } ); diff --git a/apps/webapp/test/runsReplicationService.test.ts b/apps/webapp/test/runsReplicationService.part1.test.ts similarity index 64% rename from apps/webapp/test/runsReplicationService.test.ts rename to apps/webapp/test/runsReplicationService.part1.test.ts index d11abb3248..72b2bf1440 100644 --- a/apps/webapp/test/runsReplicationService.test.ts +++ b/apps/webapp/test/runsReplicationService.part1.test.ts @@ -10,7 +10,7 @@ import superjson from "superjson"; vi.setConfig({ testTimeout: 60_000 }); -describe("RunsReplicationService", () => { +describe("RunsReplicationService (part 1/2)", () => { containerTest( "should replicate runs to clickhouse", async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { @@ -1174,605 +1174,4 @@ describe("RunsReplicationService", () => { await runsReplicationServiceB.stop(); } ); - - containerTest( - "should handover leadership to a second service, and the second service should be able to extend the leader lock", - async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { - await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); - - const clickhouse = new ClickHouse({ - url: clickhouseContainer.getConnectionUrl(), - name: "runs-replication-shutdown-handover", - }); - - // Service A - const runsReplicationServiceA = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-shutdown-handover", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 1, - flushIntervalMs: 100, - flushBatchSize: 1, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - leaderLockAcquireAdditionalTimeMs: 10_000, - ackIntervalSeconds: 5, - logger: new Logger("runs-replication-shutdown-handover-a", "debug"), - }); - - await runsReplicationServiceA.start(); - - // Service A - const runsReplicationServiceB = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-shutdown-handover", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 1, - flushIntervalMs: 100, - flushBatchSize: 1, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - leaderLockAcquireAdditionalTimeMs: 10_000, - ackIntervalSeconds: 5, - logger: new Logger("runs-replication-shutdown-handover-b", "debug"), - }); - - // Now we need to initiate starting the second service, and after 6 seconds, we need to shutdown the first service - await Promise.all([ - setTimeout(6000).then(() => runsReplicationServiceA.stop()), - runsReplicationServiceB.start(), - ]); - - const organization = await prisma.organization.create({ - data: { - title: "test", - slug: "test", - }, - }); - - const project = await prisma.project.create({ - data: { - name: "test", - slug: "test", - organizationId: organization.id, - externalRef: "test", - }, - }); - - const runtimeEnvironment = await prisma.runtimeEnvironment.create({ - data: { - slug: "test", - type: "DEVELOPMENT", - projectId: project.id, - organizationId: organization.id, - apiKey: "test", - pkApiKey: "test", - shortcode: "test", - }, - }); - - // Now we insert a row into the table - const taskRun = await prisma.taskRun.create({ - data: { - friendlyId: "run_1234", - taskIdentifier: "my-task", - payload: JSON.stringify({ foo: "bar" }), - traceId: "1234", - spanId: "1234", - queue: "test", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT", - engine: "V2", - }, - }); - - await setTimeout(10_000); - - // Check that the row was replicated to clickhouse - const queryRuns = clickhouse.reader.query({ - name: "runs-replication", - query: "SELECT * FROM trigger_dev.task_runs_v2", - schema: z.any(), - }); - - const [queryError, result] = await queryRuns({}); - - expect(queryError).toBeNull(); - expect(result?.length).toBe(1); - expect(result?.[0]).toEqual( - expect.objectContaining({ - run_id: taskRun.id, - friendly_id: taskRun.friendlyId, - task_identifier: taskRun.taskIdentifier, - environment_id: runtimeEnvironment.id, - project_id: project.id, - organization_id: organization.id, - environment_type: "DEVELOPMENT", - engine: "V2", - }) - ); - - await runsReplicationServiceB.stop(); - } - ); - - containerTest( - "should replicate all 1,000 TaskRuns inserted in bulk to ClickHouse", - async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { - await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); - - const clickhouse = new ClickHouse({ - url: clickhouseContainer.getConnectionUrl(), - name: "runs-replication-stress-bulk-insert", - }); - - const runsReplicationService = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-stress-bulk-insert", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 10, - flushIntervalMs: 100, - flushBatchSize: 50, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - ackIntervalSeconds: 5, - logger: new Logger("runs-replication-stress-bulk-insert", "info"), - }); - - await runsReplicationService.start(); - - const organization = await prisma.organization.create({ - data: { - title: "test-stress-bulk-insert", - slug: "test-stress-bulk-insert", - }, - }); - - const project = await prisma.project.create({ - data: { - name: "test-stress-bulk-insert", - slug: "test-stress-bulk-insert", - organizationId: organization.id, - externalRef: "test-stress-bulk-insert", - }, - }); - - const runtimeEnvironment = await prisma.runtimeEnvironment.create({ - data: { - slug: "test-stress-bulk-insert", - type: "DEVELOPMENT", - projectId: project.id, - organizationId: organization.id, - apiKey: "test-stress-bulk-insert", - pkApiKey: "test-stress-bulk-insert", - shortcode: "test-stress-bulk-insert", - }, - }); - - // Prepare 1,000 unique TaskRuns - const now = Date.now(); - const runsData = Array.from({ length: 1000 }, (_, i) => ({ - friendlyId: `run_bulk_${now}_${i}`, - taskIdentifier: `my-task-bulk`, - payload: JSON.stringify({ bulk: i }), - payloadType: "application/json", - traceId: `bulk-${i}`, - spanId: `bulk-${i}`, - queue: "test-stress-bulk-insert", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT" as const, - engine: "V2" as const, - status: "PENDING" as const, - attemptNumber: 1, - createdAt: new Date(now + i), - updatedAt: new Date(now + i), - })); - - // Bulk insert - const created = await prisma.taskRun.createMany({ data: runsData }); - expect(created.count).toBe(1000); - - // Wait for replication - await setTimeout(5000); - - // Query ClickHouse for all runs using FINAL - const queryRuns = clickhouse.reader.query({ - name: "runs-replication-stress-bulk-insert", - query: `SELECT run_id, friendly_id, trace_id, task_identifier FROM trigger_dev.task_runs_v2 FINAL`, - schema: z.any(), - }); - - const [queryError, result] = await queryRuns({}); - expect(queryError).toBeNull(); - expect(result?.length).toBe(1000); - - // Check a few random runs for correctness - for (let i = 0; i < 10; i++) { - const idx = Math.floor(Math.random() * 1000); - const expected = runsData[idx]; - const found = result?.find((r: any) => r.friendly_id === expected.friendlyId); - expect(found).toBeDefined(); - expect(found).toEqual( - expect.objectContaining({ - friendly_id: expected.friendlyId, - trace_id: expected.traceId, - task_identifier: expected.taskIdentifier, - }) - ); - } - - await runsReplicationService.stop(); - } - ); - - containerTest( - "should replicate all 1,000 TaskRuns inserted in bulk to ClickHouse with updates", - async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { - await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); - - const clickhouse = new ClickHouse({ - url: clickhouseContainer.getConnectionUrl(), - name: "runs-replication-stress-bulk-insert", - }); - - const runsReplicationService = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-stress-bulk-insert", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 10, - flushIntervalMs: 100, - flushBatchSize: 50, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - ackIntervalSeconds: 5, - logger: new Logger("runs-replication-stress-bulk-insert", "info"), - }); - - await runsReplicationService.start(); - - const organization = await prisma.organization.create({ - data: { - title: "test-stress-bulk-insert", - slug: "test-stress-bulk-insert", - }, - }); - - const project = await prisma.project.create({ - data: { - name: "test-stress-bulk-insert", - slug: "test-stress-bulk-insert", - organizationId: organization.id, - externalRef: "test-stress-bulk-insert", - }, - }); - - const runtimeEnvironment = await prisma.runtimeEnvironment.create({ - data: { - slug: "test-stress-bulk-insert", - type: "DEVELOPMENT", - projectId: project.id, - organizationId: organization.id, - apiKey: "test-stress-bulk-insert", - pkApiKey: "test-stress-bulk-insert", - shortcode: "test-stress-bulk-insert", - }, - }); - - // Prepare 1,000 unique TaskRuns - const now = Date.now(); - const runsData = Array.from({ length: 1000 }, (_, i) => ({ - friendlyId: `run_bulk_${now}_${i}`, - taskIdentifier: `my-task-bulk`, - payload: JSON.stringify({ bulk: i }), - payloadType: "application/json", - traceId: `bulk-${i}`, - spanId: `bulk-${i}`, - queue: "test-stress-bulk-insert", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT" as const, - engine: "V2" as const, - status: "PENDING" as const, - attemptNumber: 1, - createdAt: new Date(now + i), - updatedAt: new Date(now + i), - })); - - // Bulk insert - const created = await prisma.taskRun.createMany({ data: runsData }); - expect(created.count).toBe(1000); - - // Update all the runs - await prisma.taskRun.updateMany({ - data: { status: "COMPLETED_SUCCESSFULLY" }, - }); - - // Wait for replication - await setTimeout(5000); - - // Query ClickHouse for all runs using FINAL - const queryRuns = clickhouse.reader.query({ - name: "runs-replication-stress-bulk-insert", - query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL`, - schema: z.any(), - }); - - const [queryError, result] = await queryRuns({}); - expect(queryError).toBeNull(); - expect(result?.length).toBe(1000); - - // Check a few random runs for correctness - for (let i = 0; i < 10; i++) { - const idx = Math.floor(Math.random() * 1000); - const expected = runsData[idx]; - const found = result?.find((r: any) => r.friendly_id === expected.friendlyId); - expect(found).toBeDefined(); - expect(found).toEqual( - expect.objectContaining({ - friendly_id: expected.friendlyId, - trace_id: expected.traceId, - task_identifier: expected.taskIdentifier, - status: "COMPLETED_SUCCESSFULLY", - }) - ); - } - - await runsReplicationService.stop(); - } - ); - - containerTest( - "should replicate all events in a single transaction (insert, update)", - async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { - await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); - - const clickhouse = new ClickHouse({ - url: clickhouseContainer.getConnectionUrl(), - name: "runs-replication-multi-event-tx", - }); - - const runsReplicationService = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-multi-event-tx", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 1, - flushIntervalMs: 100, - flushBatchSize: 10, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - ackIntervalSeconds: 5, - }); - - await runsReplicationService.start(); - - const organization = await prisma.organization.create({ - data: { - title: "test-multi-event-tx", - slug: "test-multi-event-tx", - }, - }); - - const project = await prisma.project.create({ - data: { - name: "test-multi-event-tx", - slug: "test-multi-event-tx", - organizationId: organization.id, - externalRef: "test-multi-event-tx", - }, - }); - - const runtimeEnvironment = await prisma.runtimeEnvironment.create({ - data: { - slug: "test-multi-event-tx", - type: "DEVELOPMENT", - projectId: project.id, - organizationId: organization.id, - apiKey: "test-multi-event-tx", - pkApiKey: "test-multi-event-tx", - shortcode: "test-multi-event-tx", - }, - }); - - // Start a transaction - const [run1, run2] = await prisma.$transaction(async (tx) => { - const run1 = await tx.taskRun.create({ - data: { - friendlyId: `run_multi_event_1_${Date.now()}`, - taskIdentifier: "my-task-multi-event-1", - payload: JSON.stringify({ multi: 1 }), - payloadType: "application/json", - traceId: `multi-1-${Date.now()}`, - spanId: `multi-1-${Date.now()}`, - queue: "test-multi-event-tx", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT", - engine: "V2", - status: "PENDING", - attemptNumber: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - }); - const run2 = await tx.taskRun.create({ - data: { - friendlyId: `run_multi_event_2_${Date.now()}`, - taskIdentifier: "my-task-multi-event-2", - payload: JSON.stringify({ multi: 2 }), - payloadType: "application/json", - traceId: `multi-2-${Date.now()}`, - spanId: `multi-2-${Date.now()}`, - queue: "test-multi-event-tx", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT", - engine: "V2", - status: "PENDING", - attemptNumber: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - }); - await tx.taskRun.update({ - where: { id: run1.id }, - data: { status: "COMPLETED_SUCCESSFULLY" }, - }); - - return [run1, run2]; - }); - - // Wait for replication - await setTimeout(1000); - - // Query ClickHouse for both runs using FINAL - const queryRuns = clickhouse.reader.query({ - name: "runs-replication-multi-event-tx", - query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL WHERE run_id IN ({run_id_1:String}, {run_id_2:String})`, - schema: z.any(), - params: z.object({ run_id_1: z.string(), run_id_2: z.string() }), - }); - - const [queryError, result] = await queryRuns({ run_id_1: run1.id, run_id_2: run2.id }); - expect(queryError).toBeNull(); - expect(result?.length).toBe(2); - const run1Result = result?.find((r: any) => r.run_id === run1.id); - const run2Result = result?.find((r: any) => r.run_id === run2.id); - expect(run1Result).toBeDefined(); - expect(run1Result).toEqual( - expect.objectContaining({ run_id: run1.id, status: "COMPLETED_SUCCESSFULLY" }) - ); - expect(run2Result).toBeDefined(); - expect(run2Result).toEqual(expect.objectContaining({ run_id: run2.id })); - - await runsReplicationService.stop(); - } - ); - - containerTest( - "should be able to handle processing transactions for a long period of time", - async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { - await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); - - const clickhouse = new ClickHouse({ - url: clickhouseContainer.getConnectionUrl(), - name: "runs-replication-long-tx", - }); - - const runsReplicationService = new RunsReplicationService({ - clickhouse, - pgConnectionUrl: postgresContainer.getConnectionUri(), - serviceName: "runs-replication-long-tx", - slotName: "task_runs_to_clickhouse_v1", - publicationName: "task_runs_to_clickhouse_v1_publication", - redisOptions, - maxFlushConcurrency: 1, - flushIntervalMs: 100, - flushBatchSize: 10, - leaderLockTimeoutMs: 5000, - leaderLockExtendIntervalMs: 1000, - ackIntervalSeconds: 5, - logger: new Logger("runs-replication-long-tx", "info"), - }); - - await runsReplicationService.start(); - - const organization = await prisma.organization.create({ - data: { - title: "test-long-tx", - slug: "test-long-tx", - }, - }); - - const project = await prisma.project.create({ - data: { - name: "test-long-tx", - slug: "test-long-tx", - organizationId: organization.id, - externalRef: "test-long-tx", - }, - }); - - const runtimeEnvironment = await prisma.runtimeEnvironment.create({ - data: { - slug: "test-long-tx", - type: "DEVELOPMENT", - projectId: project.id, - organizationId: organization.id, - apiKey: "test-long-tx", - pkApiKey: "test-long-tx", - shortcode: "test-long-tx", - }, - }); - - // Start an interval that will create a new run every 500ms for 4 minutes - const interval = setInterval(async () => { - await prisma.taskRun.create({ - data: { - friendlyId: `run_long_tx_${Date.now()}`, - taskIdentifier: "my-task-long-tx", - payload: JSON.stringify({ long: 1 }), - payloadType: "application/json", - traceId: `long-${Date.now()}`, - spanId: `long-${Date.now()}`, - queue: "test-long-tx", - runtimeEnvironmentId: runtimeEnvironment.id, - projectId: project.id, - organizationId: organization.id, - environmentType: "DEVELOPMENT", - engine: "V2", - status: "PENDING", - attemptNumber: 1, - createdAt: new Date(), - updatedAt: new Date(), - }, - }); - }, 500); - - // Wait for 1 minute - await setTimeout(1 * 60 * 1000); - - // Stop the interval - clearInterval(interval); - - // Wait for replication - await setTimeout(1000); - - // Query ClickHouse for all runs using FINAL - const queryRuns = clickhouse.reader.query({ - name: "runs-replication-long-tx", - query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL`, - schema: z.any(), - }); - - const [queryError, result] = await queryRuns({}); - expect(queryError).toBeNull(); - - expect(result?.length).toBeGreaterThanOrEqual(50); - - await runsReplicationService.stop(); - }, - { timeout: 60_000 * 5 } - ); }); diff --git a/apps/webapp/test/runsReplicationService.part2.test.ts b/apps/webapp/test/runsReplicationService.part2.test.ts new file mode 100644 index 0000000000..01627e4ec8 --- /dev/null +++ b/apps/webapp/test/runsReplicationService.part2.test.ts @@ -0,0 +1,614 @@ +import { ClickHouse } from "@internal/clickhouse"; +import { containerTest } from "@internal/testcontainers"; +import { Logger } from "@trigger.dev/core/logger"; +import { setTimeout } from "node:timers/promises"; +import { z } from "zod"; +import { TaskRunStatus } from "~/database-types"; +import { RunsReplicationService } from "~/services/runsReplicationService.server"; +import { createInMemoryTracing } from "./utils/tracing"; +import superjson from "superjson"; + +vi.setConfig({ testTimeout: 60_000 }); + +describe("RunsReplicationService (part 2/2)", () => { + containerTest( + "should handover leadership to a second service, and the second service should be able to extend the leader lock", + async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { + await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); + + const clickhouse = new ClickHouse({ + url: clickhouseContainer.getConnectionUrl(), + name: "runs-replication-shutdown-handover", + }); + + // Service A + const runsReplicationServiceA = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-shutdown-handover", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 1, + flushIntervalMs: 100, + flushBatchSize: 1, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + leaderLockAcquireAdditionalTimeMs: 10_000, + ackIntervalSeconds: 5, + logger: new Logger("runs-replication-shutdown-handover-a", "debug"), + }); + + await runsReplicationServiceA.start(); + + // Service A + const runsReplicationServiceB = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-shutdown-handover", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 1, + flushIntervalMs: 100, + flushBatchSize: 1, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + leaderLockAcquireAdditionalTimeMs: 10_000, + ackIntervalSeconds: 5, + logger: new Logger("runs-replication-shutdown-handover-b", "debug"), + }); + + // Now we need to initiate starting the second service, and after 6 seconds, we need to shutdown the first service + await Promise.all([ + setTimeout(6000).then(() => runsReplicationServiceA.stop()), + runsReplicationServiceB.start(), + ]); + + const organization = await prisma.organization.create({ + data: { + title: "test", + slug: "test", + }, + }); + + const project = await prisma.project.create({ + data: { + name: "test", + slug: "test", + organizationId: organization.id, + externalRef: "test", + }, + }); + + const runtimeEnvironment = await prisma.runtimeEnvironment.create({ + data: { + slug: "test", + type: "DEVELOPMENT", + projectId: project.id, + organizationId: organization.id, + apiKey: "test", + pkApiKey: "test", + shortcode: "test", + }, + }); + + // Now we insert a row into the table + const taskRun = await prisma.taskRun.create({ + data: { + friendlyId: "run_1234", + taskIdentifier: "my-task", + payload: JSON.stringify({ foo: "bar" }), + traceId: "1234", + spanId: "1234", + queue: "test", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT", + engine: "V2", + }, + }); + + await setTimeout(10_000); + + // Check that the row was replicated to clickhouse + const queryRuns = clickhouse.reader.query({ + name: "runs-replication", + query: "SELECT * FROM trigger_dev.task_runs_v2", + schema: z.any(), + }); + + const [queryError, result] = await queryRuns({}); + + expect(queryError).toBeNull(); + expect(result?.length).toBe(1); + expect(result?.[0]).toEqual( + expect.objectContaining({ + run_id: taskRun.id, + friendly_id: taskRun.friendlyId, + task_identifier: taskRun.taskIdentifier, + environment_id: runtimeEnvironment.id, + project_id: project.id, + organization_id: organization.id, + environment_type: "DEVELOPMENT", + engine: "V2", + }) + ); + + await runsReplicationServiceB.stop(); + } + ); + + containerTest( + "should replicate all 1,000 TaskRuns inserted in bulk to ClickHouse", + async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { + await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); + + const clickhouse = new ClickHouse({ + url: clickhouseContainer.getConnectionUrl(), + name: "runs-replication-stress-bulk-insert", + }); + + const runsReplicationService = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-stress-bulk-insert", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 10, + flushIntervalMs: 100, + flushBatchSize: 50, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + ackIntervalSeconds: 5, + logger: new Logger("runs-replication-stress-bulk-insert", "info"), + }); + + await runsReplicationService.start(); + + const organization = await prisma.organization.create({ + data: { + title: "test-stress-bulk-insert", + slug: "test-stress-bulk-insert", + }, + }); + + const project = await prisma.project.create({ + data: { + name: "test-stress-bulk-insert", + slug: "test-stress-bulk-insert", + organizationId: organization.id, + externalRef: "test-stress-bulk-insert", + }, + }); + + const runtimeEnvironment = await prisma.runtimeEnvironment.create({ + data: { + slug: "test-stress-bulk-insert", + type: "DEVELOPMENT", + projectId: project.id, + organizationId: organization.id, + apiKey: "test-stress-bulk-insert", + pkApiKey: "test-stress-bulk-insert", + shortcode: "test-stress-bulk-insert", + }, + }); + + // Prepare 1,000 unique TaskRuns + const now = Date.now(); + const runsData = Array.from({ length: 1000 }, (_, i) => ({ + friendlyId: `run_bulk_${now}_${i}`, + taskIdentifier: `my-task-bulk`, + payload: JSON.stringify({ bulk: i }), + payloadType: "application/json", + traceId: `bulk-${i}`, + spanId: `bulk-${i}`, + queue: "test-stress-bulk-insert", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT" as const, + engine: "V2" as const, + status: "PENDING" as const, + attemptNumber: 1, + createdAt: new Date(now + i), + updatedAt: new Date(now + i), + })); + + // Bulk insert + const created = await prisma.taskRun.createMany({ data: runsData }); + expect(created.count).toBe(1000); + + // Wait for replication + await setTimeout(5000); + + // Query ClickHouse for all runs using FINAL + const queryRuns = clickhouse.reader.query({ + name: "runs-replication-stress-bulk-insert", + query: `SELECT run_id, friendly_id, trace_id, task_identifier FROM trigger_dev.task_runs_v2 FINAL`, + schema: z.any(), + }); + + const [queryError, result] = await queryRuns({}); + expect(queryError).toBeNull(); + expect(result?.length).toBe(1000); + + // Check a few random runs for correctness + for (let i = 0; i < 10; i++) { + const idx = Math.floor(Math.random() * 1000); + const expected = runsData[idx]; + const found = result?.find((r: any) => r.friendly_id === expected.friendlyId); + expect(found).toBeDefined(); + expect(found).toEqual( + expect.objectContaining({ + friendly_id: expected.friendlyId, + trace_id: expected.traceId, + task_identifier: expected.taskIdentifier, + }) + ); + } + + await runsReplicationService.stop(); + } + ); + + containerTest( + "should replicate all 1,000 TaskRuns inserted in bulk to ClickHouse with updates", + async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { + await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); + + const clickhouse = new ClickHouse({ + url: clickhouseContainer.getConnectionUrl(), + name: "runs-replication-stress-bulk-insert", + }); + + const runsReplicationService = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-stress-bulk-insert", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 10, + flushIntervalMs: 100, + flushBatchSize: 50, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + ackIntervalSeconds: 5, + logger: new Logger("runs-replication-stress-bulk-insert", "info"), + }); + + await runsReplicationService.start(); + + const organization = await prisma.organization.create({ + data: { + title: "test-stress-bulk-insert", + slug: "test-stress-bulk-insert", + }, + }); + + const project = await prisma.project.create({ + data: { + name: "test-stress-bulk-insert", + slug: "test-stress-bulk-insert", + organizationId: organization.id, + externalRef: "test-stress-bulk-insert", + }, + }); + + const runtimeEnvironment = await prisma.runtimeEnvironment.create({ + data: { + slug: "test-stress-bulk-insert", + type: "DEVELOPMENT", + projectId: project.id, + organizationId: organization.id, + apiKey: "test-stress-bulk-insert", + pkApiKey: "test-stress-bulk-insert", + shortcode: "test-stress-bulk-insert", + }, + }); + + // Prepare 1,000 unique TaskRuns + const now = Date.now(); + const runsData = Array.from({ length: 1000 }, (_, i) => ({ + friendlyId: `run_bulk_${now}_${i}`, + taskIdentifier: `my-task-bulk`, + payload: JSON.stringify({ bulk: i }), + payloadType: "application/json", + traceId: `bulk-${i}`, + spanId: `bulk-${i}`, + queue: "test-stress-bulk-insert", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT" as const, + engine: "V2" as const, + status: "PENDING" as const, + attemptNumber: 1, + createdAt: new Date(now + i), + updatedAt: new Date(now + i), + })); + + // Bulk insert + const created = await prisma.taskRun.createMany({ data: runsData }); + expect(created.count).toBe(1000); + + // Update all the runs + await prisma.taskRun.updateMany({ + data: { status: "COMPLETED_SUCCESSFULLY" }, + }); + + // Wait for replication + await setTimeout(5000); + + // Query ClickHouse for all runs using FINAL + const queryRuns = clickhouse.reader.query({ + name: "runs-replication-stress-bulk-insert", + query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL`, + schema: z.any(), + }); + + const [queryError, result] = await queryRuns({}); + expect(queryError).toBeNull(); + expect(result?.length).toBe(1000); + + // Check a few random runs for correctness + for (let i = 0; i < 10; i++) { + const idx = Math.floor(Math.random() * 1000); + const expected = runsData[idx]; + const found = result?.find((r: any) => r.friendly_id === expected.friendlyId); + expect(found).toBeDefined(); + expect(found).toEqual( + expect.objectContaining({ + friendly_id: expected.friendlyId, + trace_id: expected.traceId, + task_identifier: expected.taskIdentifier, + status: "COMPLETED_SUCCESSFULLY", + }) + ); + } + + await runsReplicationService.stop(); + } + ); + + containerTest( + "should replicate all events in a single transaction (insert, update)", + async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { + await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); + + const clickhouse = new ClickHouse({ + url: clickhouseContainer.getConnectionUrl(), + name: "runs-replication-multi-event-tx", + }); + + const runsReplicationService = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-multi-event-tx", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 1, + flushIntervalMs: 100, + flushBatchSize: 10, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + ackIntervalSeconds: 5, + }); + + await runsReplicationService.start(); + + const organization = await prisma.organization.create({ + data: { + title: "test-multi-event-tx", + slug: "test-multi-event-tx", + }, + }); + + const project = await prisma.project.create({ + data: { + name: "test-multi-event-tx", + slug: "test-multi-event-tx", + organizationId: organization.id, + externalRef: "test-multi-event-tx", + }, + }); + + const runtimeEnvironment = await prisma.runtimeEnvironment.create({ + data: { + slug: "test-multi-event-tx", + type: "DEVELOPMENT", + projectId: project.id, + organizationId: organization.id, + apiKey: "test-multi-event-tx", + pkApiKey: "test-multi-event-tx", + shortcode: "test-multi-event-tx", + }, + }); + + // Start a transaction + const [run1, run2] = await prisma.$transaction(async (tx) => { + const run1 = await tx.taskRun.create({ + data: { + friendlyId: `run_multi_event_1_${Date.now()}`, + taskIdentifier: "my-task-multi-event-1", + payload: JSON.stringify({ multi: 1 }), + payloadType: "application/json", + traceId: `multi-1-${Date.now()}`, + spanId: `multi-1-${Date.now()}`, + queue: "test-multi-event-tx", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT", + engine: "V2", + status: "PENDING", + attemptNumber: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + }); + const run2 = await tx.taskRun.create({ + data: { + friendlyId: `run_multi_event_2_${Date.now()}`, + taskIdentifier: "my-task-multi-event-2", + payload: JSON.stringify({ multi: 2 }), + payloadType: "application/json", + traceId: `multi-2-${Date.now()}`, + spanId: `multi-2-${Date.now()}`, + queue: "test-multi-event-tx", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT", + engine: "V2", + status: "PENDING", + attemptNumber: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + }); + await tx.taskRun.update({ + where: { id: run1.id }, + data: { status: "COMPLETED_SUCCESSFULLY" }, + }); + + return [run1, run2]; + }); + + // Wait for replication + await setTimeout(1000); + + // Query ClickHouse for both runs using FINAL + const queryRuns = clickhouse.reader.query({ + name: "runs-replication-multi-event-tx", + query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL WHERE run_id IN ({run_id_1:String}, {run_id_2:String})`, + schema: z.any(), + params: z.object({ run_id_1: z.string(), run_id_2: z.string() }), + }); + + const [queryError, result] = await queryRuns({ run_id_1: run1.id, run_id_2: run2.id }); + expect(queryError).toBeNull(); + expect(result?.length).toBe(2); + const run1Result = result?.find((r: any) => r.run_id === run1.id); + const run2Result = result?.find((r: any) => r.run_id === run2.id); + expect(run1Result).toBeDefined(); + expect(run1Result).toEqual( + expect.objectContaining({ run_id: run1.id, status: "COMPLETED_SUCCESSFULLY" }) + ); + expect(run2Result).toBeDefined(); + expect(run2Result).toEqual(expect.objectContaining({ run_id: run2.id })); + + await runsReplicationService.stop(); + } + ); + + containerTest( + "should be able to handle processing transactions for a long period of time", + async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => { + await prisma.$executeRawUnsafe(`ALTER TABLE public."TaskRun" REPLICA IDENTITY FULL;`); + + const clickhouse = new ClickHouse({ + url: clickhouseContainer.getConnectionUrl(), + name: "runs-replication-long-tx", + }); + + const runsReplicationService = new RunsReplicationService({ + clickhouse, + pgConnectionUrl: postgresContainer.getConnectionUri(), + serviceName: "runs-replication-long-tx", + slotName: "task_runs_to_clickhouse_v1", + publicationName: "task_runs_to_clickhouse_v1_publication", + redisOptions, + maxFlushConcurrency: 1, + flushIntervalMs: 100, + flushBatchSize: 10, + leaderLockTimeoutMs: 5000, + leaderLockExtendIntervalMs: 1000, + ackIntervalSeconds: 5, + logger: new Logger("runs-replication-long-tx", "info"), + }); + + await runsReplicationService.start(); + + const organization = await prisma.organization.create({ + data: { + title: "test-long-tx", + slug: "test-long-tx", + }, + }); + + const project = await prisma.project.create({ + data: { + name: "test-long-tx", + slug: "test-long-tx", + organizationId: organization.id, + externalRef: "test-long-tx", + }, + }); + + const runtimeEnvironment = await prisma.runtimeEnvironment.create({ + data: { + slug: "test-long-tx", + type: "DEVELOPMENT", + projectId: project.id, + organizationId: organization.id, + apiKey: "test-long-tx", + pkApiKey: "test-long-tx", + shortcode: "test-long-tx", + }, + }); + + // Start an interval that will create a new run every 500ms for 4 minutes + const interval = setInterval(async () => { + await prisma.taskRun.create({ + data: { + friendlyId: `run_long_tx_${Date.now()}`, + taskIdentifier: "my-task-long-tx", + payload: JSON.stringify({ long: 1 }), + payloadType: "application/json", + traceId: `long-${Date.now()}`, + spanId: `long-${Date.now()}`, + queue: "test-long-tx", + runtimeEnvironmentId: runtimeEnvironment.id, + projectId: project.id, + organizationId: organization.id, + environmentType: "DEVELOPMENT", + engine: "V2", + status: "PENDING", + attemptNumber: 1, + createdAt: new Date(), + updatedAt: new Date(), + }, + }); + }, 500); + + // Wait for 1 minute + await setTimeout(1 * 60 * 1000); + + // Stop the interval + clearInterval(interval); + + // Wait for replication + await setTimeout(1000); + + // Query ClickHouse for all runs using FINAL + const queryRuns = clickhouse.reader.query({ + name: "runs-replication-long-tx", + query: `SELECT * FROM trigger_dev.task_runs_v2 FINAL`, + schema: z.any(), + }); + + const [queryError, result] = await queryRuns({}); + expect(queryError).toBeNull(); + + expect(result?.length).toBeGreaterThanOrEqual(50); + + await runsReplicationService.stop(); + }, + { timeout: 60_000 * 5 } + ); +}); diff --git a/internal-packages/clickhouse/package.json b/internal-packages/clickhouse/package.json index 3bfe84f762..e2767708eb 100644 --- a/internal-packages/clickhouse/package.json +++ b/internal-packages/clickhouse/package.json @@ -14,9 +14,7 @@ }, "devDependencies": { "@internal/testcontainers": "workspace:*", - "@vitest/coverage-v8": "^3.0.8", - "rimraf": "6.0.1", - "vitest": "^3.0.8" + "rimraf": "6.0.1" }, "scripts": { "clean": "rimraf dist", diff --git a/internal-packages/redis/package.json b/internal-packages/redis/package.json index 1bd9a146d9..9c13bbf21b 100644 --- a/internal-packages/redis/package.json +++ b/internal-packages/redis/package.json @@ -9,9 +9,6 @@ "ioredis": "^5.3.2", "@trigger.dev/core": "workspace:*" }, - "devDependencies": { - "vitest": "^1.4.0" - }, "scripts": { "typecheck": "tsc --noEmit" } diff --git a/internal-packages/replication/package.json b/internal-packages/replication/package.json index 6f5206d7fc..3aedabb3ed 100644 --- a/internal-packages/replication/package.json +++ b/internal-packages/replication/package.json @@ -14,9 +14,7 @@ }, "devDependencies": { "@internal/testcontainers": "workspace:*", - "@vitest/coverage-v8": "^3.0.8", "rimraf": "6.0.1", - "vitest": "^3.0.8", "@types/pg": "8.11.14" }, "scripts": { diff --git a/internal-packages/run-engine/package.json b/internal-packages/run-engine/package.json index f760b9f065..e509d2dede 100644 --- a/internal-packages/run-engine/package.json +++ b/internal-packages/run-engine/package.json @@ -35,9 +35,7 @@ "devDependencies": { "@internal/testcontainers": "workspace:*", "@types/seedrandom": "^3.0.8", - "@vitest/coverage-v8": "^3.0.8", - "rimraf": "6.0.1", - "vitest": "^3.0.8" + "rimraf": "6.0.1" }, "scripts": { "clean": "rimraf dist", diff --git a/internal-packages/testcontainers/package.json b/internal-packages/testcontainers/package.json index 56c8b84202..9ff640ade5 100644 --- a/internal-packages/testcontainers/package.json +++ b/internal-packages/testcontainers/package.json @@ -16,8 +16,7 @@ "@trigger.dev/core": "workspace:*", "std-env": "^3.9.0", "testcontainers": "^10.25.0", - "tinyexec": "^0.3.0", - "vitest": "^1.4.0" + "tinyexec": "^0.3.0" }, "scripts": { "typecheck": "tsc --noEmit" diff --git a/internal-packages/tracing/package.json b/internal-packages/tracing/package.json index 60aa0ba8b4..e3a7635d74 100644 --- a/internal-packages/tracing/package.json +++ b/internal-packages/tracing/package.json @@ -11,9 +11,6 @@ "@opentelemetry/semantic-conventions": "^1.27.0", "@trigger.dev/core": "workspace:*" }, - "devDependencies": { - "vitest": "^1.4.0" - }, "scripts": { "typecheck": "tsc --noEmit" } diff --git a/internal-packages/zod-worker/package.json b/internal-packages/zod-worker/package.json index 444ea2fc10..1d9cbad93c 100644 --- a/internal-packages/zod-worker/package.json +++ b/internal-packages/zod-worker/package.json @@ -14,8 +14,7 @@ }, "devDependencies": { "@types/lodash.omit": "^4.5.7", - "@types/pg": "8.6.6", - "vitest": "^1.4.0" + "@types/pg": "8.6.6" }, "scripts": { "typecheck": "tsc --noEmit" diff --git a/package.json b/package.json index 569e322f96..bb1f180320 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@playwright/test": "^1.36.2", "@trigger.dev/database": "workspace:*", "@types/node": "20.14.14", + "@vitest/coverage-v8": "3.1.4", "autoprefixer": "^10.4.12", "eslint-plugin-turbo": "^2.0.4", "lefthook": "^1.11.3", @@ -64,7 +65,7 @@ "typescript": "5.5.4", "vite": "^4.1.1", "vite-tsconfig-paths": "^4.0.5", - "vitest": "^0.28.4" + "vitest": "3.1.4" }, "packageManager": "pnpm@8.15.5", "dependencies": { diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 77abfab6e9..c8652d448d 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -65,8 +65,7 @@ "rimraf": "^5.0.7", "ts-essentials": "10.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0", - "vitest": "^2.0.5" + "tsx": "4.17.0" }, "scripts": { "clean": "rimraf dist .tshy .tshy-build .turbo", diff --git a/packages/core/package.json b/packages/core/package.json index 427f6e6c1a..dc982076c5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -212,8 +212,7 @@ "rimraf": "^3.0.2", "ts-essentials": "10.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0", - "vitest": "^1.6.0" + "tsx": "4.17.0" }, "engines": { "node": ">=18.20.0" diff --git a/packages/redis-worker/package.json b/packages/redis-worker/package.json index b506a7167d..728b41fc77 100644 --- a/packages/redis-worker/package.json +++ b/packages/redis-worker/package.json @@ -37,8 +37,7 @@ "@types/lodash.omit": "^4.5.7", "rimraf": "6.0.1", "tsup": "^8.4.0", - "tsx": "4.17.0", - "vitest": "^1.4.0" + "tsx": "4.17.0" }, "engines": { "node": ">=18.20.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be23fd12d9..57d142b076 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: '@types/node': specifier: 20.14.14 version: 20.14.14 + '@vitest/coverage-v8': + specifier: 3.1.4 + version: 3.1.4(vitest@3.1.4) autoprefixer: specifier: ^10.4.12 version: 10.4.13(postcss@8.5.3) @@ -81,8 +84,8 @@ importers: specifier: ^4.0.5 version: 4.0.5(typescript@5.5.4) vitest: - specifier: ^0.28.4 - version: 0.28.5 + specifier: 3.1.4 + version: 3.1.4(@types/node@20.14.14) apps/coordinator: dependencies: @@ -180,9 +183,6 @@ importers: '@types/dockerode': specifier: ^3.3.33 version: 3.3.35 - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) apps/webapp: dependencies: @@ -853,9 +853,6 @@ importers: vite-tsconfig-paths: specifier: ^4.0.5 version: 4.0.5(typescript@5.5.4) - vitest: - specifier: ^1.4.0 - version: 1.4.0(@types/node@20.14.14) docs: {} @@ -880,15 +877,9 @@ importers: '@internal/testcontainers': specifier: workspace:* version: link:../testcontainers - '@vitest/coverage-v8': - specifier: ^3.0.8 - version: 3.0.8(vitest@3.0.8) rimraf: specifier: 6.0.1 version: 6.0.1 - vitest: - specifier: ^3.0.8 - version: 3.0.8(@types/node@20.14.14) internal-packages/database: dependencies: @@ -967,10 +958,6 @@ importers: ioredis: specifier: ^5.3.2 version: 5.3.2 - devDependencies: - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) internal-packages/replication: dependencies: @@ -996,15 +983,9 @@ importers: '@types/pg': specifier: 8.11.14 version: 8.11.14 - '@vitest/coverage-v8': - specifier: ^3.0.8 - version: 3.0.8(vitest@3.0.8) rimraf: specifier: 6.0.1 version: 6.0.1 - vitest: - specifier: ^3.0.8 - version: 3.0.8(@types/node@20.14.14) internal-packages/run-engine: dependencies: @@ -1048,15 +1029,9 @@ importers: '@types/seedrandom': specifier: ^3.0.8 version: 3.0.8 - '@vitest/coverage-v8': - specifier: ^3.0.8 - version: 3.0.8(vitest@3.0.8) rimraf: specifier: 6.0.1 version: 6.0.1 - vitest: - specifier: ^3.0.8 - version: 3.0.8(@types/node@20.14.14) internal-packages/testcontainers: dependencies: @@ -1091,9 +1066,6 @@ importers: tinyexec: specifier: ^0.3.0 version: 0.3.0 - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) internal-packages/tracing: dependencies: @@ -1109,10 +1081,6 @@ importers: '@trigger.dev/core': specifier: workspace:* version: link:../../packages/core - devDependencies: - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) internal-packages/zod-worker: dependencies: @@ -1141,9 +1109,6 @@ importers: '@types/pg': specifier: 8.6.6 version: 8.6.6 - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) packages/build: dependencies: @@ -1413,9 +1378,6 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 - vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@20.14.14)(supports-color@10.0.0) packages/core: dependencies: @@ -1555,9 +1517,6 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 - vitest: - specifier: ^1.6.0 - version: 1.6.0(@types/node@20.14.14) packages/python: dependencies: @@ -1672,9 +1631,6 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 - vitest: - specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.14) packages/rsc: dependencies: @@ -2811,7 +2767,7 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 /@andrewbranch/untar.js@1.0.3: @@ -4243,14 +4199,6 @@ packages: dependencies: '@babel/types': 7.24.7 - /@babel/parser@7.26.8: - resolution: {integrity: sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.27.0 - dev: true - /@babel/parser@7.27.0: resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} engines: {node: '>=6.0.0'} @@ -4510,14 +4458,6 @@ packages: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@babel/types@7.26.8: - resolution: {integrity: sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - dev: true - /@babel/types@7.27.0: resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} @@ -7883,13 +7823,6 @@ packages: engines: {node: '>=8'} dev: true - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} @@ -7938,6 +7871,7 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false /@jridgewell/sourcemap-codec@1.5.0: resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -15760,10 +15694,6 @@ packages: resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} dev: false - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true - /@sinclair/typebox@0.33.17: resolution: {integrity: sha512-75232GRx3wp3P7NP+yc4nRK3XUAnaQShxTAzapgmQrgs0QvSq0/mOJGoZXRpH15cFCKyys+4laCPbBselqJ5Ag==} dev: false @@ -17268,20 +17198,6 @@ packages: resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} dev: false - /@types/chai-subset@1.3.3: - resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} - dependencies: - '@types/chai': 4.3.6 - dev: true - - /@types/chai@4.3.4: - resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} - dev: true - - /@types/chai@4.3.6: - resolution: {integrity: sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==} - dev: true - /@types/compression@1.7.2: resolution: {integrity: sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==} dependencies: @@ -18286,11 +18202,11 @@ packages: - utf-8-validate dev: false - /@vitest/coverage-v8@3.0.8(vitest@3.0.8): - resolution: {integrity: sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==} + /@vitest/coverage-v8@3.1.4(vitest@3.1.4): + resolution: {integrity: sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==} peerDependencies: - '@vitest/browser': 3.0.8 - vitest: 3.0.8 + '@vitest/browser': 3.1.4 + vitest: 3.1.4 peerDependenciesMeta: '@vitest/browser': optional: true @@ -18304,58 +18220,25 @@ packages: istanbul-reports: 3.1.7 magic-string: 0.30.17 magicast: 0.3.5 - std-env: 3.8.1 + std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.8(@types/node@20.14.14) + vitest: 3.1.4(@types/node@20.14.14) transitivePeerDependencies: - supports-color dev: true - /@vitest/expect@0.28.5: - resolution: {integrity: sha512-gqTZwoUTwepwGIatnw4UKpQfnoyV0Z9Czn9+Lo2/jLIt4/AXLTn+oVZxlQ7Ng8bzcNkR+3DqLJ08kNr8jRmdNQ==} - dependencies: - '@vitest/spy': 0.28.5 - '@vitest/utils': 0.28.5 - chai: 4.4.1 - dev: true - - /@vitest/expect@1.4.0: - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + /@vitest/expect@3.1.4: + resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==} dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - chai: 4.4.1 - dev: true - - /@vitest/expect@1.6.0: - resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} - dependencies: - '@vitest/spy': 1.6.0 - '@vitest/utils': 1.6.0 - chai: 4.4.1 - dev: true - - /@vitest/expect@2.0.5: - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} - dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - tinyrainbow: 1.2.0 - dev: true - - /@vitest/expect@3.0.8: - resolution: {integrity: sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==} - dependencies: - '@vitest/spy': 3.0.8 - '@vitest/utils': 3.0.8 + '@vitest/spy': 3.1.4 + '@vitest/utils': 3.1.4 chai: 5.2.0 tinyrainbow: 2.0.0 dev: true - /@vitest/mocker@3.0.8(vite@5.2.7): - resolution: {integrity: sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==} + /@vitest/mocker@3.1.4(vite@5.2.7): + resolution: {integrity: sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -18365,165 +18248,43 @@ packages: vite: optional: true dependencies: - '@vitest/spy': 3.0.8 + '@vitest/spy': 3.1.4 estree-walker: 3.0.3 magic-string: 0.30.17 vite: 5.2.7(@types/node@20.14.14) dev: true - /@vitest/pretty-format@2.0.5: - resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} - dependencies: - tinyrainbow: 1.2.0 - dev: true - - /@vitest/pretty-format@3.0.8: - resolution: {integrity: sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==} + /@vitest/pretty-format@3.1.4: + resolution: {integrity: sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==} dependencies: tinyrainbow: 2.0.0 dev: true - /@vitest/runner@0.28.5: - resolution: {integrity: sha512-NKkHtLB+FGjpp5KmneQjTcPLWPTDfB7ie+MmF1PnUBf/tGe2OjGxWyB62ySYZ25EYp9krR5Bw0YPLS/VWh1QiA==} + /@vitest/runner@3.1.4: + resolution: {integrity: sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==} dependencies: - '@vitest/utils': 0.28.5 - p-limit: 4.0.0 - pathe: 1.1.2 - dev: true - - /@vitest/runner@1.4.0: - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} - dependencies: - '@vitest/utils': 1.4.0 - p-limit: 5.0.0 - pathe: 1.1.2 - dev: true - - /@vitest/runner@1.6.0: - resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} - dependencies: - '@vitest/utils': 1.6.0 - p-limit: 5.0.0 - pathe: 1.1.2 - dev: true - - /@vitest/runner@2.0.5: - resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} - dependencies: - '@vitest/utils': 2.0.5 - pathe: 1.1.2 - dev: true - - /@vitest/runner@3.0.8: - resolution: {integrity: sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==} - dependencies: - '@vitest/utils': 3.0.8 + '@vitest/utils': 3.1.4 pathe: 2.0.3 dev: true - /@vitest/snapshot@1.4.0: - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} - dependencies: - magic-string: 0.30.17 - pathe: 1.1.2 - pretty-format: 29.7.0 - dev: true - - /@vitest/snapshot@1.6.0: - resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} - dependencies: - magic-string: 0.30.17 - pathe: 1.1.2 - pretty-format: 29.7.0 - dev: true - - /@vitest/snapshot@2.0.5: - resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + /@vitest/snapshot@3.1.4: + resolution: {integrity: sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==} dependencies: - '@vitest/pretty-format': 2.0.5 - magic-string: 0.30.17 - pathe: 1.1.2 - dev: true - - /@vitest/snapshot@3.0.8: - resolution: {integrity: sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==} - dependencies: - '@vitest/pretty-format': 3.0.8 + '@vitest/pretty-format': 3.1.4 magic-string: 0.30.17 pathe: 2.0.3 dev: true - /@vitest/spy@0.28.5: - resolution: {integrity: sha512-7if6rsHQr9zbmvxN7h+gGh2L9eIIErgf8nSKYDlg07HHimCxp4H6I/X/DPXktVPPLQfiZ1Cw2cbDIx9fSqDjGw==} - dependencies: - tinyspy: 1.0.2 - dev: true - - /@vitest/spy@1.4.0: - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} - dependencies: - tinyspy: 2.2.1 - dev: true - - /@vitest/spy@1.6.0: - resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} - dependencies: - tinyspy: 2.2.1 - dev: true - - /@vitest/spy@2.0.5: - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} - dependencies: - tinyspy: 3.0.0 - dev: true - - /@vitest/spy@3.0.8: - resolution: {integrity: sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==} + /@vitest/spy@3.1.4: + resolution: {integrity: sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==} dependencies: tinyspy: 3.0.2 dev: true - /@vitest/utils@0.28.5: - resolution: {integrity: sha512-UyZdYwdULlOa4LTUSwZ+Paz7nBHGTT72jKwdFSV4IjHF1xsokp+CabMdhjvVhYwkLfO88ylJT46YMilnkSARZA==} - dependencies: - cli-truncate: 3.1.0 - diff: 5.1.0 - loupe: 2.3.7 - picocolors: 1.1.1 - pretty-format: 27.5.1 - dev: true - - /@vitest/utils@1.4.0: - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + /@vitest/utils@3.1.4: + resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - dev: true - - /@vitest/utils@1.6.0: - resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - dev: true - - /@vitest/utils@2.0.5: - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} - dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 - loupe: 3.1.1 - tinyrainbow: 1.2.0 - dev: true - - /@vitest/utils@3.0.8: - resolution: {integrity: sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==} - dependencies: - '@vitest/pretty-format': 3.0.8 + '@vitest/pretty-format': 3.1.4 loupe: 3.1.3 tinyrainbow: 2.0.0 dev: true @@ -18971,12 +18732,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - /acorn@8.8.1: - resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - /adm-zip@0.5.16: resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} engines: {node: '>=12.0'} @@ -18985,6 +18740,7 @@ packages: /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} + requiresBuild: true dependencies: debug: 4.4.0(supports-color@10.0.0) transitivePeerDependencies: @@ -19568,10 +19324,6 @@ packages: engines: {node: '>=0.8'} dev: false - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true - /assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -20271,43 +20023,6 @@ packages: /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - /chai@4.3.7: - resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} - engines: {node: '>=4'} - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.3 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.0.8 - dev: true - - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.3 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.0.8 - dev: true - - /chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} - engines: {node: '>=12'} - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.1 - pathval: 2.0.0 - dev: true - /chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -20372,12 +20087,6 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: false - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - dependencies: - get-func-name: 2.0.2 - dev: true - /check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -20571,14 +20280,6 @@ packages: string-width: 4.2.3 dev: true - /cli-truncate@3.1.0: - resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - slice-ansi: 5.0.0 - string-width: 5.1.2 - dev: true - /cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -21357,6 +21058,7 @@ packages: dependencies: ms: 2.1.2 supports-color: 10.0.0 + dev: false /debug@4.3.7(supports-color@10.0.0): resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} @@ -21418,13 +21120,6 @@ packages: mimic-response: 3.1.0 dev: false - /deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} - engines: {node: '>=6'} - dependencies: - type-detect: 4.0.8 - dev: true - /deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -21619,11 +21314,6 @@ packages: /diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -22091,10 +21781,6 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - /es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - dev: true - /es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -23261,8 +22947,8 @@ packages: engines: {node: '>=6'} dev: false - /expect-type@1.2.0: - resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} + /expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} dev: true @@ -23525,6 +23211,17 @@ packages: dependencies: picomatch: 4.0.2 + /fdir@6.4.4(picomatch@4.0.2): + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 + dev: true + /fflate@0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} dev: false @@ -23934,10 +23631,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true - /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -25007,11 +24700,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - dev: true - /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} @@ -25409,10 +25097,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: true - /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -25893,24 +25577,11 @@ packages: engines: {node: '>= 12.13.0'} dev: true - /local-pkg@0.4.2: - resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} - engines: {node: '>=14'} - dev: true - /local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} dev: true - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} - dependencies: - mlly: 1.7.1 - pkg-types: 1.1.3 - dev: true - /locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} @@ -26034,18 +25705,6 @@ packages: dependencies: js-tokens: 4.0.0 - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - dependencies: - get-func-name: 2.0.2 - dev: true - - /loupe@3.1.1: - resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} - dependencies: - get-func-name: 2.0.2 - dev: true - /loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} dev: true @@ -26140,12 +25799,6 @@ packages: sourcemap-codec: 1.4.8 dev: false - /magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} dependencies: @@ -26156,6 +25809,7 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + dev: false /magicast@0.3.4: resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} @@ -26168,8 +25822,8 @@ packages: /magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} dependencies: - '@babel/parser': 7.26.8 - '@babel/types': 7.26.8 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 source-map-js: 1.2.1 dev: true @@ -28496,13 +28150,6 @@ packages: yocto-queue: 1.1.1 dev: true - /p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - dependencies: - yocto-queue: 1.1.1 - dev: true - /p-limit@6.2.0: resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} engines: {node: '>=18'} @@ -28790,24 +28437,12 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - /pathe@1.1.0: - resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} - dev: true - - /pathe@1.1.1: - resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} - dev: true - /pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} /pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true - /pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -29582,15 +29217,6 @@ packages: react-is: 17.0.2 dev: true - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - dev: true - /pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} @@ -30255,10 +29881,6 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true - /react-markdown@10.1.0(@types/react@19.0.12)(react@19.0.0): resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: @@ -31788,14 +31410,6 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true - /slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - dev: true - /slug@6.1.0: resolution: {integrity: sha512-x6vLHCMasg4DR2LPiyFGI0gJJhywY6DTiGhCrOMzb3SOk/0JVLIaL4UhyFSHu04SD3uAavrKY/K3zZ3i6iRcgA==} dev: false @@ -32201,6 +31815,7 @@ packages: /std-env@3.8.1: resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} + dev: false /std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -32397,18 +32012,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - /strip-literal@1.0.1: - resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} - dependencies: - acorn: 8.12.1 - dev: true - - /strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} - dependencies: - js-tokens: 9.0.0 - dev: true - /stripe@12.18.0: resolution: {integrity: sha512-cYjgBM2SY/dTm8Lr6eMyyONaHTZHA/QjHxFUIW5WH8FevSRIGAVtXEmBkUXF1fsqe7QvvRgQSGSJZmjDacegGg==} engines: {node: '>=12.*'} @@ -33141,14 +32744,6 @@ packages: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} dev: false - /tinybench@2.3.1: - resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} - dev: true - - /tinybench@2.6.0: - resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} - dev: true - /tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} dev: true @@ -33183,6 +32778,14 @@ packages: picomatch: 4.0.2 dev: true + /tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + dev: true + /tinyglobby@0.2.2: resolution: {integrity: sha512-mZ2sDMaySvi1PkTp4lTo1In2zjU+cY8OvZsfwrDrx3YGRbXPX1/cbPwCR9zkm3O/Fz9Jo0F1HNgIQ1b8BepqyQ==} engines: {node: '>=12.0.0'} @@ -33198,51 +32801,16 @@ packages: tinycolor2: 1.6.0 dev: false - /tinypool@0.3.1: - resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==} - engines: {node: '>=14.0.0'} - dev: true - - /tinypool@0.8.3: - resolution: {integrity: sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==} - engines: {node: '>=14.0.0'} - dev: true - - /tinypool@1.0.1: - resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} - engines: {node: ^18.0.0 || >=20.0.0} - dev: true - /tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} dev: true - /tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - dev: true - /tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} dev: true - /tinyspy@1.0.2: - resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} - engines: {node: '>=14.0.0'} - dev: true - - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true - - /tinyspy@3.0.0: - resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} - engines: {node: '>=14.0.0'} - dev: true - /tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -33751,11 +33319,6 @@ packages: dependencies: prelude-ls: 1.2.1 - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true - /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -34612,77 +34175,14 @@ packages: - terser dev: true - /vite-node@1.4.0(@types/node@20.14.14): - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.4.0(supports-color@10.0.0) - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.2.7(@types/node@20.14.14) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite-node@1.6.0(@types/node@20.14.14): - resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.4.0(supports-color@10.0.0) - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.2.7(@types/node@20.14.14) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite-node@2.0.5(@types/node@20.14.14)(supports-color@10.0.0): - resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.4.0(supports-color@10.0.0) - pathe: 1.1.2 - tinyrainbow: 1.2.0 - vite: 5.2.7(@types/node@20.14.14) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite-node@3.0.8(@types/node@20.14.14): - resolution: {integrity: sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==} + /vite-node@3.1.4(@types/node@20.14.14): + resolution: {integrity: sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@10.0.0) - es-module-lexer: 1.6.0 + es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 5.2.7(@types/node@20.14.14) transitivePeerDependencies: @@ -34813,239 +34313,16 @@ packages: fsevents: 2.3.3 dev: true - /vitest@0.28.5: - resolution: {integrity: sha512-pyCQ+wcAOX7mKMcBNkzDwEHRGqQvHUl0XnoHR+3Pb1hytAHISgSxv9h0gUiSiYtISXUU3rMrKiKzFYDrI6ZIHA==} - engines: {node: '>=v14.16.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/chai': 4.3.4 - '@types/chai-subset': 1.3.3 - '@types/node': 20.14.14 - '@vitest/expect': 0.28.5 - '@vitest/runner': 0.28.5 - '@vitest/spy': 0.28.5 - '@vitest/utils': 0.28.5 - acorn: 8.8.1 - acorn-walk: 8.2.0 - cac: 6.7.14 - chai: 4.3.7 - debug: 4.3.4 - local-pkg: 0.4.2 - pathe: 1.1.0 - picocolors: 1.0.0 - source-map: 0.6.1 - std-env: 3.8.1 - strip-literal: 1.0.1 - tinybench: 2.3.1 - tinypool: 0.3.1 - tinyspy: 1.0.2 - vite: 4.1.4(@types/node@20.14.14) - vite-node: 0.28.5(@types/node@20.14.14) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vitest@1.4.0(@types/node@20.14.14): - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 20.14.14 - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.8 - pathe: 1.1.1 - picocolors: 1.0.0 - std-env: 3.8.1 - strip-literal: 2.1.0 - tinybench: 2.6.0 - tinypool: 0.8.3 - vite: 5.2.7(@types/node@20.14.14) - vite-node: 1.4.0(@types/node@20.14.14) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vitest@1.6.0(@types/node@20.14.14): - resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.6.0 - '@vitest/ui': 1.6.0 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 20.14.14 - '@vitest/expect': 1.6.0 - '@vitest/runner': 1.6.0 - '@vitest/snapshot': 1.6.0 - '@vitest/spy': 1.6.0 - '@vitest/utils': 1.6.0 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.4.0(supports-color@10.0.0) - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.17 - pathe: 1.1.2 - picocolors: 1.1.1 - std-env: 3.9.0 - strip-literal: 2.1.0 - tinybench: 2.9.0 - tinypool: 0.8.3 - vite: 5.2.7(@types/node@20.14.14) - vite-node: 1.6.0(@types/node@20.14.14) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vitest@2.0.5(@types/node@20.14.14)(supports-color@10.0.0): - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@ampproject/remapping': 2.3.0 - '@types/node': 20.14.14 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - debug: 4.3.6(supports-color@10.0.0) - execa: 8.0.1 - magic-string: 0.30.11 - pathe: 1.1.2 - std-env: 3.8.1 - tinybench: 2.9.0 - tinypool: 1.0.1 - tinyrainbow: 1.2.0 - vite: 5.2.7(@types/node@20.14.14) - vite-node: 2.0.5(@types/node@20.14.14)(supports-color@10.0.0) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vitest@3.0.8(@types/node@20.14.14): - resolution: {integrity: sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==} + /vitest@3.1.4(@types/node@20.14.14): + resolution: {integrity: sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.8 - '@vitest/ui': 3.0.8 + '@vitest/browser': 3.1.4 + '@vitest/ui': 3.1.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -35065,25 +34342,26 @@ packages: optional: true dependencies: '@types/node': 20.14.14 - '@vitest/expect': 3.0.8 - '@vitest/mocker': 3.0.8(vite@5.2.7) - '@vitest/pretty-format': 3.0.8 - '@vitest/runner': 3.0.8 - '@vitest/snapshot': 3.0.8 - '@vitest/spy': 3.0.8 - '@vitest/utils': 3.0.8 + '@vitest/expect': 3.1.4 + '@vitest/mocker': 3.1.4(vite@5.2.7) + '@vitest/pretty-format': 3.1.4 + '@vitest/runner': 3.1.4 + '@vitest/snapshot': 3.1.4 + '@vitest/spy': 3.1.4 + '@vitest/utils': 3.1.4 chai: 5.2.0 debug: 4.4.0(supports-color@10.0.0) - expect-type: 1.2.0 + expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 - std-env: 3.8.1 + std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 + tinyglobby: 0.2.13 tinypool: 1.0.2 tinyrainbow: 2.0.0 vite: 5.2.7(@types/node@20.14.14) - vite-node: 3.0.8(@types/node@20.14.14) + vite-node: 3.1.4(@types/node@20.14.14) why-is-node-running: 2.3.0 transitivePeerDependencies: - less @@ -35371,15 +34649,6 @@ packages: isexe: 3.1.1 dev: false - /why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} - engines: {node: '>=8'} - hasBin: true - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - dev: true - /why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'}