diff --git a/.gitignore b/.gitignore
index 2949f9cd..7970b779 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,9 @@ yarn-error.log*
 lerna-debug.log*
 .DS_Store
 
+# test profiling data
+profiling
+
 # Diagnostic reports (https://nodejs.org/api/report.html)
 report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
 
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 3d769e4e..fa9c1e18 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -77,39 +77,16 @@
             "console": "integratedTerminal"
         },
         {
-            "name": "Debug Jest Tests",
+            // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
             "type": "node",
             "request": "launch",
-            "env": {
-                "NODE_OPTIONS": "--experimental-vm-modules"
-            },
-            "runtimeArgs": [
-                "--inspect-brk",
-                "${workspaceRoot}/node_modules/.bin/jest",
-                "--runInBand",
-                "history"
-            ],
-            "console": "integratedTerminal",
-            "internalConsoleOptions": "neverOpen"
-        },
-        {
-            "type": "node",
-            "name": "vscode-jest-tests.v2",
-            "request": "launch",
-            "env": {
-                "NODE_OPTIONS": "--experimental-vm-modules"
-            },
-            "args": [
-                "${workspaceRoot}/node_modules/.bin/jest",
-                "--runInBand",
-                "--watchAll=false",
-                "--testNamePattern",
-                "${jest.testNamePattern}",
-                "--runTestsByPath",
-                "${jest.testFile}"
-            ],
-            "console": "integratedTerminal",
-            "internalConsoleOptions": "neverOpen"
+            "name": "Debug Tests",
+            "autoAttachChildProcesses": true,
+            "skipFiles": ["<node_internals>/**", "**/node_modules/**"],
+            "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
+            "args": ["run"],
+            "smartStep": true,
+            "console": "integratedTerminal"
         }
     ]
 }
\ No newline at end of file
diff --git a/jest.config.cjs b/jest.config.cjs
deleted file mode 100644
index 29335854..00000000
--- a/jest.config.cjs
+++ /dev/null
@@ -1,19 +0,0 @@
-module.exports = {
-  testTimeout: 2 * 60 * 1000,
-  moduleNameMapper: {
-    '^(\\.{1,2}/.*)\\.js$': '$1'
-  },
-  extensionsToTreatAsEsm: ['.ts'],
-  transform: {
-    '^.+\\.(mt|t|cj|j)s$': [
-      'ts-jest',
-      {
-        useESM: true
-      }
-    ]
-  },
-  testEnvironment: 'node',
-  testMatch: [
-    '<rootDir>/**/__tests__/*.ts'
-  ]
-}
diff --git a/package.json b/package.json
index eb9421e0..5a58e223 100644
--- a/package.json
+++ b/package.json
@@ -8,21 +8,17 @@
   "license": "AGPL-3.0-or-later",
   "devDependencies": {
     "@types/auth0": "^3.3.2",
-    "@types/jest": "^29.4.0",
     "@types/node": "^18.13.0",
     "@types/supertest": "^2.0.12",
     "@types/underscore": "^1.11.4",
     "cross-env": "^7.0.3",
     "husky": "^8.0.1",
-    "jest": "^29.7.0",
-    "jest-extended": "^4.0.2",
     "mongodb-memory-server": "^10.1.2",
     "nock": "^13.3.0",
     "supertest": "^6.3.3",
-    "ts-jest": "^29.2.5",
     "ts-standard": "^12.0.0",
     "typescript": "4.9.5",
-    "wait-for-expect": "^3.0.2"
+    "vitest": "^2.1.8"
   },
   "dependencies": {
     "@apollo/server": "^4.11.2",
@@ -73,7 +69,7 @@
   "scripts": {
     "lint": "yarn ts-standard",
     "fix": "yarn ts-standard --fix",
-    "test": "cross-env NODE_OPTIONS=\"--experimental-vm-modules\" jest --runInBand",
+    "test": "LOG_LEVEL=error vitest run --silent",
     "build": "tsc -p tsconfig.json",
     "build-release": "tsc  -p tsconfig.release.json",
     "clean": "tsc -b --clean && rm -rf build/*",
diff --git a/src/__tests__/areas.ts b/src/__tests__/areas.ts
index aea41608..7b06ce59 100644
--- a/src/__tests__/areas.ts
+++ b/src/__tests__/areas.ts
@@ -1,76 +1,60 @@
-import { ApolloServer } from '@apollo/server'
-import muuid from 'uuid-mongodb'
-import { jest } from '@jest/globals'
-import MutableAreaDataSource from '../model/MutableAreaDataSource.js'
-import MutableMediaDataSource from '../model/MutableMediaDataSource.js'
-import MutableOrganizationDataSource from '../model/MutableOrganizationDataSource.js'
 import { MediaObjectGQLInput } from '../db/MediaObjectTypes.js'
 import { AreaType } from '../db/AreaTypes.js'
 import { OrganizationEditableFieldsType, OrganizationType, OrgType } from '../db/OrganizationTypes.js'
-import { queryAPI, setUpServer } from '../utils/testUtils.js'
 import { muuidToString } from '../utils/helpers.js'
-import { InMemoryDB } from '../utils/inMemoryDB.js'
-import express from 'express'
+import { gqlTest } from './fixtures/gql.fixtures.js'
+import gql from 'graphql-tag'
+interface LocalContext {
+  includedChild: AreaType
+  excludedArea: AreaType
+  alphaFields: OrganizationEditableFieldsType
+  alphaOrg: OrganizationType
+  insertMedia: (mediaCount: number, areaId?: string, user?: string) => Promise<void>
+}
 
-jest.setTimeout(60000)
-
-describe('areas API', () => {
-  let server: ApolloServer
-  let user: muuid.MUUID
-  let userUuid: string
-  let app: express.Application
-  let inMemoryDB: InMemoryDB
-
-  // Mongoose models for mocking pre-existing state.
-  let areas: MutableAreaDataSource
-  let organizations: MutableOrganizationDataSource
-  let usa: AreaType
-  let ca: AreaType
-  let wa: AreaType
-
-  beforeAll(async () => {
-    ({ server, inMemoryDB, app } = await setUpServer())
-    // Auth0 serializes uuids in "relaxed" mode, resulting in this hex string format
-    // "59f1d95a-627d-4b8c-91b9-389c7424cb54" instead of base64 "WfHZWmJ9S4yRuTicdCTLVA==".
-    user = muuid.mode('relaxed').v4()
-    userUuid = muuidToString(user)
-  })
-
-  beforeEach(async () => {
-    await inMemoryDB.clear()
-    areas = MutableAreaDataSource.getInstance()
-    organizations = MutableOrganizationDataSource.getInstance()
-    usa = await areas.addCountry('usa')
-    ca = await areas.addArea(user, 'CA', usa.metadata.area_id)
-    wa = await areas.addArea(user, 'WA', usa.metadata.area_id)
-  })
+const it = gqlTest.extend<LocalContext>({
+  includedChild: async ({ addArea, area }, use) => await use(await addArea(undefined, { parent: area })),
+  excludedArea: async ({ addArea, area }, use) => await use(await addArea(undefined, { parent: area })),
+  alphaFields: async ({ excludedArea, task, area }, use) => await use({
+    displayName: task.id,
+    associatedAreaIds: [area.metadata.area_id],
+    excludedAreaIds: [excludedArea.metadata.area_id]
+  }),
+  alphaOrg: async ({ organizations, user, alphaFields }, use) => {
+    const org = await organizations.addOrganization(user, OrgType.localClimbingOrganization, alphaFields)
+      .then((res: OrganizationType | null) => {
+        if (res === null) throw new Error('Failure mocking organization.')
+        return res
+      })
 
-  afterAll(async () => {
-    await server.stop()
-    await inMemoryDB.close()
-  })
+    await use(org)
+    await organizations.deleteFromCacheById(org._id)
+  },
+  insertMedia: async ({ task, area, userUuid, media }, use) => {
+    async function insertMediaObjectsForArea (mediaCount: number, areaId: string = muuidToString(area.metadata.area_id), user = userUuid): Promise<void> {
+      const newMediaListInput: MediaObjectGQLInput[] = []
+      for (let picIndex = 0; picIndex < mediaCount; picIndex++) {
+        newMediaListInput.push({
+          userUuid: user,
+          width: 800,
+          height: 600,
+          format: 'jpeg',
+          size: 45000,
+          mediaUrl: `/areaPhoto${areaId}-${picIndex}.jpg`,
+          entityTag: {
+            entityType: 1,
+            entityId: areaId
+          }
+        })
+      }
 
-  async function insertMediaObjectsForArea (areaId: string, mediaCount: number): Promise<void> {
-    const newMediaListInput: MediaObjectGQLInput[] = []
-    for (let i = 0; i < mediaCount; i++) {
-      newMediaListInput.push({
-        userUuid: 'a2eb6353-65d1-445f-912c-53c6301404bd',
-        width: 800,
-        height: 600,
-        format: 'jpeg',
-        size: 45000,
-        mediaUrl: `/areaPhoto${i}.jpg`,
-        entityTag: {
-          entityType: 1,
-          entityId: areaId
-        }
-      })
+      await media.addMediaObjects(newMediaListInput)
     }
-
-    const media = MutableMediaDataSource.getInstance()
-    await media.addMediaObjects(newMediaListInput)
+    await use(insertMediaObjectsForArea)
   }
+})
 
+describe('areas API', () => {
   describe('queries', () => {
     const areaQuery = `
       query area($input: ID) {
@@ -82,59 +66,58 @@ describe('areas API', () => {
         }
       }
     `
-    let alphaFields: OrganizationEditableFieldsType
-    let alphaOrg: OrganizationType
 
-    beforeEach(async () => {
-      alphaFields = {
-        displayName: 'USA without CA Org',
-        associatedAreaIds: [usa.metadata.area_id],
-        excludedAreaIds: [ca.metadata.area_id]
-      }
-      alphaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, alphaFields)
-        .then((res: OrganizationType | null) => {
-          if (res === null) throw new Error('Failure mocking organization.')
-          return res
-        })
-    })
-
-    it('retrieves an area omitting organizations that exclude it', async () => {
-      const response = await queryAPI({
+    it('retrieves an area omitting organizations that exclude it', async ({ query, userUuid, excludedArea }) => {
+      const response = await query({
         query: areaQuery,
         operationName: 'area',
-        variables: { input: ca.metadata.area_id },
-        userUuid,
-        app
+        variables: { input: muuidToString(excludedArea.metadata.area_id) },
+        userUuid
       })
+
       expect(response.statusCode).toBe(200)
       const areaResult = response.body.data.area
-      expect(areaResult.uuid).toBe(muuidToString(ca.metadata.area_id))
+      expect(areaResult).toBeTruthy()
+      expect(areaResult.uuid).toBe(muuidToString(excludedArea.metadata.area_id))
       // Even though alphaOrg associates with ca's parent, usa, it excludes
       // ca and so should not be listed.
       expect(areaResult.organizations).toHaveLength(0)
     })
 
-    it.each([userUuid, undefined])('retrieves an area and lists associated organizations', async (userId) => {
-      const response = await queryAPI({
+    it('retrieves an area and lists associated organizations', async ({ query, userUuid, includedChild, alphaOrg }) => {
+      const response = await query({
+        query: areaQuery,
+        operationName: 'area',
+        variables: { input: muuidToString(includedChild.metadata.area_id) },
+        userUuid
+      })
+
+      expect(response.statusCode).toBe(200)
+      const areaResult = response.body.data.area
+      expect(areaResult.uuid).toBe(muuidToString(includedChild.metadata.area_id))
+      expect(areaResult.organizations).toHaveLength(1)
+      expect(areaResult.organizations[0].orgId).toBe(muuidToString(alphaOrg.orgId))
+    })
+
+    it('retrieves an area and lists associated organizations, even with no auth context', async ({ query, includedChild, alphaOrg }) => {
+      const response = await query({
         query: areaQuery,
         operationName: 'area',
-        variables: { input: wa.metadata.area_id },
-        userUuid: userId,
-        app
+        variables: { input: muuidToString(includedChild.metadata.area_id) }
       })
 
       expect(response.statusCode).toBe(200)
       const areaResult = response.body.data.area
-      expect(areaResult.uuid).toBe(muuidToString(wa.metadata.area_id))
+      expect(areaResult.uuid).toBe(muuidToString(includedChild.metadata.area_id))
       expect(areaResult.organizations).toHaveLength(1)
       expect(areaResult.organizations[0].orgId).toBe(muuidToString(alphaOrg.orgId))
     })
   })
 
-  it('returns paginated Media when requested', async () => {
-    await insertMediaObjectsForArea(usa.metadata.area_id.toString(), 11)
+  it('returns paginated Media when requested', async ({ area, query, insertMedia }) => {
+    await insertMedia(11)
 
-    const areaQueryWithPaginatedMedia = `
+    const areaQueryWithPaginatedMedia = gql`
       query area($uuid: ID!, $input: EmbeddedAreaMediaInput) {
         area(uuid: $uuid) {
           mediaPagination(input: $input) {
@@ -157,18 +140,16 @@ describe('areas API', () => {
         }
       }
     `
-    const response = await queryAPI({
+    const response = await query({
       query: areaQueryWithPaginatedMedia,
       operationName: 'area',
       variables: {
-        uuid: usa.metadata.area_id,
+        uuid: area.metadata.area_id,
         input: {
           first: 5,
           after: null
         }
-      },
-      userUuid,
-      app
+      }
     })
     expect(response.statusCode).toBe(200)
     const areaResult = response.body.data.area
diff --git a/src/__tests__/bulkImport.test.ts b/src/__tests__/bulkImport.test.ts
index be8a54c3..d17a313d 100644
--- a/src/__tests__/bulkImport.test.ts
+++ b/src/__tests__/bulkImport.test.ts
@@ -1,17 +1,27 @@
-import {ApolloServer} from "@apollo/server";
 import muuid from "uuid-mongodb";
-import express from "express";
-import {InMemoryDB} from "../utils/inMemoryDB.js";
-import {queryAPI, setUpServer} from "../utils/testUtils.js";
-import {muuidToString} from "../utils/helpers.js";
 import exampleImportData from './import-example.json' assert {type: 'json'};
-import {AreaType} from "../db/AreaTypes.js";
 import {BulkImportResultType} from "../db/BulkImportTypes.js";
-import MutableClimbDataSource from "../model/MutableClimbDataSource.js";
-import BulkImportDataSource from "../model/BulkImportDataSource.js";
+import { gqlTest   } from "./fixtures/gql.fixtures.js";
+import { muuidToString } from "../utils/helpers";
+
+interface LocalContext {
+  importData: typeof exampleImportData
+}
+
+const it = gqlTest.extend<LocalContext>({
+  importData: async ({ country }, use) => await use(
+    { areas: exampleImportData.areas.map(x => {
+      if (x.countryCode) {
+        return { ...x, countryCode: country.shortCode}
+      }
+
+      return { ...x }
+    }) as typeof exampleImportData['areas']
+  })
+})
 
 describe('bulkImportAreas', () => {
-  const query = `
+  const bulkQuery = `
     mutation bulkImportAreas($input: BulkImportInput!) {
       bulkImportAreas(input: $input) {
         addedAreas {
@@ -33,85 +43,50 @@ describe('bulkImportAreas', () => {
     }
   `
 
-  let server: ApolloServer
-  let user: muuid.MUUID
-  let userUuid: string
-  let app: express.Application
-  let inMemoryDB: InMemoryDB
-  let testArea: AreaType
-
-  let bulkImport: BulkImportDataSource
-  let climbs: MutableClimbDataSource
-
-  beforeAll(async () => {
-    ({server, inMemoryDB, app} = await setUpServer())
-    // Auth0 serializes uuids in "relaxed" mode, resulting in this hex string format
-    // "59f1d95a-627d-4b8c-91b9-389c7424cb54" instead of base64 "WfHZWmJ9S4yRuTicdCTLVA==".
-    user = muuid.mode('relaxed').v4()
-    userUuid = muuidToString(user)
-    bulkImport = BulkImportDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
-  })
-
-  beforeEach(async () => {
-    await inMemoryDB.clear()
-    await bulkImport.addCountry('usa')
-    testArea = await bulkImport.addArea(user, "Test Area", null, "us")
-  })
-
-  afterAll(async () => {
-    await server.stop()
-    await inMemoryDB.close()
-  })
-
-  it('should return 403 if no user', async () => {
-    const res = await queryAPI({
-      app,
-      query,
+  it('should return 403 if no user', async ( { query, importData }) => {
+    const res = await query({
+      query: bulkQuery,
       operationName: 'bulkImportAreas',
-      variables: {input: exampleImportData}
+      variables: {input: importData}
     })
     expect(res.statusCode).toBe(200)
     expect(res.body.errors[0].message).toBe('Not Authorised!')
   })
 
-  it('should return 403 if user is not an editor', async () => {
-    const res = await queryAPI({
-      app,
+  it('should return 403 if user is not an editor', async ({ query, userUuid, importData }) => {
+    const res = await query({
       userUuid,
-      query,
+      query: bulkQuery,
       operationName: 'bulkImportAreas',
-      variables: {input: exampleImportData}
+      variables: {input: importData}
     })
     expect(res.statusCode).toBe(200)
     expect(res.body.errors[0].message).toBe('Not Authorised!')
   })
 
-  it('should return 200 if user is an editor', async () => {
-    const res = await queryAPI({
-      app,
+  it('should return 200 if user is an editor', async ({ query, importData, userUuid}) => {
+    const res = await query({
       userUuid,
       roles: ['editor'],
-      query,
+      query: bulkQuery,
       operationName: 'bulkImportAreas',
-      variables: {input: exampleImportData}
+      variables: {input: importData}
     })
     expect(res.status).toBe(200)
   })
 
-  it('should import data', async () => {
-    const res = await queryAPI({
-      app,
+  it('should import data', async ({ query, userUuid, area, bulkImport, climbs, importData }) => {
+    const res = await query({
       userUuid,
       roles: ['editor'],
-      query,
+      query: bulkQuery,
       operationName: 'bulkImportAreas',
       variables: {
         input: {
           areas: [
-            ...exampleImportData.areas,
+            ...importData.areas,
             {
-              uuid: testArea.metadata.area_id,
+              uuid: muuidToString(area.metadata.area_id),
               areaName: "Updated Test Area",
             }
           ]
diff --git a/src/__tests__/fixtures/data.fixtures.ts b/src/__tests__/fixtures/data.fixtures.ts
new file mode 100644
index 00000000..03e5555c
--- /dev/null
+++ b/src/__tests__/fixtures/data.fixtures.ts
@@ -0,0 +1,224 @@
+import {
+  ClimbChangeInputType,
+  ClimbType,
+  DisciplineType
+} from '../../db/ClimbTypes'
+import { AreaType } from '../../db/AreaTypes'
+import { dbTest } from './mongo.fixtures'
+import muuid, { MUUID } from 'uuid-mongodb'
+import { muuidToString } from '../../utils/helpers'
+import isoCountries, { Alpha3Code } from 'i18n-iso-countries'
+import { UserPublicProfile } from '../../db/UserTypes'
+import { createGradeObject, gradeContextToGradeScales } from '../../GradeUtils'
+import { getScale, GradeScalesTypes } from '@openbeta/sandbag'
+import CountriesLngLat from '../../data/countries-with-lnglat.json'
+import { TickStyle, TickAttemptType } from '../../db/TickTypes'
+
+export const allowableStyleMap: Record<TickStyle, TickAttemptType[]> = {
+  Lead: ['Onsight', 'Flash', 'Redpoint', 'Pinkpoint', 'Attempt', 'Frenchfree'],
+  Solo: ['Onsight', 'Flash', 'Redpoint', 'Attempt'],
+  Boulder: ['Flash', 'Send', 'Attempt'],
+  TR: ['Send', 'Attempt'],
+  Follow: ['Send', 'Attempt'],
+  Aid: ['Send', 'Attempt']
+}
+
+export function choose<T> (arr: T[]): T {
+  return arr[Math.floor(Math.random() * arr.length)]
+}
+
+interface DbTestContext {
+  user: MUUID
+  userUuid: string
+  profile: UserPublicProfile
+
+  addArea: (
+    name?: string,
+    extra?: Partial<{
+      leaf: boolean
+      boulder: boolean
+      parent: MUUID | AreaType
+    }>,
+  ) => Promise<AreaType>
+  countryCode: Alpha3Code
+  country: AreaType
+  area: AreaType
+
+  addClimb: (props?: Partial<ClimbChangeInputType>) => Promise<ClimbType>
+  climb: ClimbType
+
+  gradeSystemFor: (climb: { type: DisciplineType }) => GradeScalesTypes
+
+  /**
+   * Given the country that has been supplied to this test context, what would be a
+   * valid grade to generate for a given climb object
+   */
+  randomGrade: (climb: ClimbType | { type: DisciplineType }) => string
+}
+
+const availableCountries: Alpha3Code[] = Object.keys(
+  isoCountries.getAlpha3Codes()
+).filter((country) => CountriesLngLat[country]) as Alpha3Code[]
+
+beforeAll(() => {
+  // We set a default grade contexts for all countries
+  for (const country of availableCountries) {
+    if (gradeContextToGradeScales[country] !== undefined) continue
+    gradeContextToGradeScales[country] = gradeContextToGradeScales.US
+  }
+})
+
+export const dataFixtures = dbTest.extend<DbTestContext>({
+  user: async ({ task }, use) => await use(muuid.v4()),
+  userUuid: async ({ user }, use) => await use(muuidToString(user)),
+  profile: async ({ task, user, users, userUuid }, use) => {
+    await users.createOrUpdateUserProfile(user, {
+      userUuid,
+      username: task.id,
+      email: 'cat@example.com'
+    })
+
+    const profile = await users.getUserPublicProfileByUuid(user)
+    assert(profile != null)
+    await use(profile)
+
+    await users.deleteFromCacheByFields({ username: task.id })
+  },
+
+  countryCode: async ({ task }, use) => {
+    const countryCode = availableCountries.pop()
+    assert(countryCode !== undefined)
+    await use(countryCode)
+  },
+
+  country: async ({ areas, countryCode }, use) => {
+    const country = await areas.addCountry(countryCode)
+    assert(country.shortCode)
+    gradeContextToGradeScales[country.shortCode] = gradeContextToGradeScales.US
+    await use(country)
+
+    await areas.areaModel.deleteMany({
+      'embeddedRelations.ancestors._id': country._id
+    })
+    await areas.areaModel.deleteOne({ _id: country._id })
+    // once we have cleared out this country and its children, we can happily add this
+    // country code back into the stack
+    availableCountries.push(countryCode)
+  },
+
+  addArea: async ({ task, country, user, areas }, use) => {
+    async function addArea (
+      name?: string,
+      extra?: Partial<{
+        leaf: boolean
+        boulder: boolean
+        parent: MUUID | AreaType
+      }>
+    ): Promise<AreaType> {
+      function isArea (x: any): x is AreaType {
+        return typeof x.metadata?.area_id !== 'undefined'
+      }
+
+      if (name === undefined || name === 'test') {
+        name = task.id + process.uptime().toString()
+      }
+
+      let parent: MUUID | undefined
+      if (extra?.parent != null) {
+        if (isArea(extra.parent)) {
+          parent = extra.parent.metadata?.area_id
+        } else {
+          parent = extra.parent
+        }
+      }
+
+      return await areas.addArea(
+        user,
+        name,
+        parent ?? country.metadata.area_id,
+        undefined,
+        undefined,
+        extra?.leaf,
+        extra?.boulder
+      )
+    }
+
+    await use(addArea)
+
+    await areas.areaModel.deleteMany({ area_name: { $regex: `^${task.id}` } })
+  },
+  area: async ({ addArea }, use) => {
+    await use(await addArea())
+  },
+
+  addClimb: async ({ climbs, area, task, user }, use) => {
+    async function addClimb (
+      data?: Partial<ClimbChangeInputType>
+    ): Promise<ClimbType> {
+      const [id] = await climbs.addOrUpdateClimbs(user, area.metadata.area_id, [
+        {
+          name: task.id + process.uptime().toString(),
+          disciplines: data?.disciplines ?? {
+            sport: true
+          },
+          description: 'A good warm up problem',
+          location: 'Start from the left arete',
+          protection: '2 bolts',
+          boltsCount: 2,
+          ...(data ?? {})
+        }
+      ])
+
+      const climb = await climbs.findOneClimbByMUUID(muuid.from(id))
+      assert(climb != null)
+      return climb
+    }
+
+    await use(addClimb)
+
+    await climbs.climbModel.deleteMany({
+      name: { $regex: `^${task.id}` }
+    })
+  },
+  climb: async ({ addClimb }, use) => {
+    await use(await addClimb())
+  },
+
+  gradeSystemFor: async ({ country }, use) => {
+    const ctx = gradeContextToGradeScales[country.gradeContext]
+    assert(ctx !== undefined)
+
+    const generate = (climb: { type: DisciplineType }): GradeScalesTypes => {
+      const key = Object.keys(climb.type).filter((type) => climb.type[type])[0]
+      const system: GradeScalesTypes =
+        gradeContextToGradeScales[country.gradeContext]?.[key]
+      assert(system)
+      return system
+    }
+
+    await use(generate)
+  },
+
+  randomGrade: async ({ country, gradeSystemFor }, use) => {
+    const ctx = gradeContextToGradeScales[country.gradeContext]
+    assert(ctx !== undefined)
+
+    const generate = (climb: ClimbType): string => {
+      const system = gradeSystemFor(climb)
+
+      const scale = getScale(system)
+      assert(scale, `no support for system ${system}`)
+      const grade = scale.getGrade(Math.floor(Math.random() * 100))
+      assert(grade)
+
+      const record = createGradeObject(grade, climb.type, ctx)
+      assert(record !== undefined)
+
+      const first = record[Object.keys(record)[0]]
+      assert(first)
+      return first
+    }
+
+    await use(generate)
+  }
+})
diff --git a/src/__tests__/fixtures/gql.fixtures.ts b/src/__tests__/fixtures/gql.fixtures.ts
new file mode 100644
index 00000000..3b3925ea
--- /dev/null
+++ b/src/__tests__/fixtures/gql.fixtures.ts
@@ -0,0 +1,134 @@
+import { ApolloServer, BaseContext } from '@apollo/server'
+import express, { Application } from 'express'
+import request from 'supertest'
+import jwt from 'jsonwebtoken'
+import { expressMiddleware } from '@apollo/server/express4'
+import bodyParser from 'body-parser'
+import { applyMiddleware } from 'graphql-middleware'
+import { graphqlSchema } from '../../graphql/resolvers'
+import cors from 'cors'
+import { dataFixtures } from './data.fixtures'
+import { createContext, permissions } from '../../auth'
+import { ASTNode, print } from 'graphql'
+
+let server: ApolloServer<BaseContext>
+let app: Application
+
+interface ServerTestFixtures {
+  ctx: {
+    server: ApolloServer<BaseContext>
+    app: Application
+  }
+  query: (opts: QueryAPIProps) => Promise<request.Response>
+}
+
+export interface QueryAPIProps {
+  query?: string | ASTNode
+  operationName?: string
+  variables?: any
+  userUuid?: string
+  roles?: string[]
+  port?: number
+  endpoint?: string
+  app?: express.Application
+  body?: any
+}
+
+export const gqlTest = dataFixtures.extend<ServerTestFixtures>({
+  ctx: async ({
+    climbs, areas, bulkImport,
+    organizations,
+    ticks,
+    history,
+    media,
+    users
+  }, use) => {
+    if (app === undefined) {
+      app = express()
+    }
+
+    if (server === undefined) {
+      const schema = applyMiddleware(
+        graphqlSchema,
+        permissions.generate(graphqlSchema)
+      )
+
+      const dataSources = ({
+        climbs,
+        areas,
+        bulkImport,
+        organizations,
+        ticks,
+        history,
+        media,
+        users
+      })
+
+      server = new ApolloServer({
+        schema,
+        introspection: false,
+        plugins: []
+      })
+
+      // server must be started before applying middleware
+      await server.start()
+
+      app.use('/',
+        bodyParser.json({ limit: '10mb' }),
+        cors<cors.CorsRequest>(),
+        express.json(),
+        expressMiddleware(server, {
+          context: async ({ req }) => ({ dataSources, ...await createContext({ req }) })
+        })
+      )
+    }
+
+    await use({
+      app, server
+    })
+  },
+
+  query: async ({ ctx }, use) => {
+    await use(
+      async ({
+        query,
+        operationName,
+        variables,
+        userUuid,
+        roles = [],
+        endpoint = '/'
+      }: QueryAPIProps) => {
+        // Avoid needing to pass in actual signed tokens.
+        const jwtSpy = vi.spyOn(jwt, 'verify')
+
+        jwtSpy.mockImplementation(() => {
+          return {
+            // Roles defined at https://manage.auth0.com/dashboard/us/dev-fmjy7n5n/roles
+            'https://tacos.openbeta.io/roles': roles,
+            'https://tacos.openbeta.io/uuid': userUuid
+          }
+        })
+
+        // It can be convienient to pass in the results of some
+        // gql`dksjfhsdkjf` as in many IDE's this will provide
+        // syntax highlighting and will create a more useful error
+        // message when a mistake in the query literal is made.
+        if (query !== undefined && typeof query !== 'string') {
+          query = print(query)
+        }
+
+        const queryObj = { query, operationName, variables }
+
+        let req = request(ctx.app)
+          .post(endpoint)
+          .send(queryObj)
+
+        if (userUuid != null) {
+          req = req.set('Authorization', 'Bearer placeholder-jwt-see-SpyOn')
+        }
+
+        return await req
+      }
+    )
+  }
+})
diff --git a/src/__tests__/fixtures/mongo.fixtures.ts b/src/__tests__/fixtures/mongo.fixtures.ts
new file mode 100644
index 00000000..ba39b3b9
--- /dev/null
+++ b/src/__tests__/fixtures/mongo.fixtures.ts
@@ -0,0 +1,147 @@
+/* eslint-disable no-empty-pattern */
+// To explain the rule for this file: Object destructuring is REQUIRED for vitest fixtures because
+// of how they utilize autoloading.
+import { MongoMemoryReplSet } from 'mongodb-memory-server'
+import { ChangeStream, MongoClient } from 'mongodb'
+import mongoose from 'mongoose'
+import { checkVar, defaultPostConnect } from '../../db'
+import MutableAreaDataSource from '../../model/MutableAreaDataSource'
+import MutableClimbDataSource from '../../model/MutableClimbDataSource'
+import BulkImportDataSource from '../../model/BulkImportDataSource'
+import ChangeLogDataSource from '../../model/ChangeLogDataSource'
+import MutableMediaDataSource from '../../model/MutableMediaDataSource'
+import MutableOrganizationDataSource from '../../model/MutableOrganizationDataSource'
+import TickDataSource from '../../model/TickDataSource'
+import UserDataSource from '../../model/UserDataSource'
+import { MUUID } from 'uuid-mongodb'
+import { BaseChangeRecordType, ChangeLogType } from '../../db/ChangeLogType'
+import { logger } from '../../logger'
+
+/**
+ * In-memory Mongo replset used for testing.
+ * More portable than requiring user to set up Mongo in a background Docker process.
+ * Need a replset to faciliate transactions.
+ */
+let mongod: MongoMemoryReplSet
+let uri: string
+let _stream: ChangeStream
+
+beforeAll(async () => {
+  mongod = await MongoMemoryReplSet.create({
+    // Stream listener listens on DB denoted by 'MONGO_DBNAME' env var.
+    replSet: { count: 1, storageEngine: 'wiredTiger', dbName: checkVar('MONGO_DBNAME') }
+  })
+
+  uri = await mongod.getUri(checkVar('MONGO_DBNAME'))
+  await mongoose.connect(uri, { autoIndex: false })
+  mongoose.set('debug', false) // Set to 'true' to enable verbose mode
+  _stream = await defaultPostConnect()
+  _stream.on('change', (doc) => {
+    // Dummy consumer
+  })
+})
+
+afterAll(async () => {
+  if (_stream.listeners.length > 0) {
+    logger.info(`Trailing listeners ${_stream.listeners.length}`)
+  }
+})
+
+interface DbTestContext {
+  uri: string
+  client: MongoClient
+  insertDirectly: (collection: string, documents: any[]) => Promise<void>
+
+  areas: MutableAreaDataSource
+  climbs: MutableClimbDataSource
+  bulkImport: BulkImportDataSource
+  organizations: MutableOrganizationDataSource
+  ticks: TickDataSource
+  history: ChangeLogDataSource
+  media: MutableMediaDataSource
+  users: UserDataSource
+  changeLog: ChangeLogDataSource
+
+  waitForChanges: (props: WaitProps) => Promise<void>
+}
+
+export const dbTest = test.extend<DbTestContext>({
+  uri: async ({ }, use) => await use(uri),
+  client: async ({ uri }, use) => {
+    const client = new MongoClient(uri)
+    await use(client)
+    await client.close()
+  },
+
+  insertDirectly: async ({ task, uri }, use) => {
+    /**
+     * Bypass Mongoose to insert data directly into Mongo.
+     * Useful for inserting data that is incompatible with Mongoose schemas for migration testing.
+     * @param collection Name of collection for documents to be inserted into.
+     * @param docs Documents to be inserted into collection.
+     */
+    const insertDirectly = async (collection: string, documents: any[]): Promise<void> => {
+      const client = new MongoClient(uri)
+
+      try {
+        const database = client.db(task.id)
+        const mCollection = database.collection(collection)
+        const result = await mCollection.insertMany(documents)
+
+        logger.debug(`${result.insertedCount} documents were inserted directly into MongoDB`)
+      } finally {
+        await client.close()
+      }
+    }
+
+    await use(insertDirectly)
+  },
+
+  areas: async ({ }, use) => await use(MutableAreaDataSource.getInstance()),
+  climbs: async ({ }, use) => await use(MutableClimbDataSource.getInstance()),
+  bulkImport: async ({ }, use) => await use(BulkImportDataSource.getInstance()),
+  organizations: async ({ }, use) => await use(MutableOrganizationDataSource.getInstance()),
+  ticks: async ({ }, use) => await use(TickDataSource.getInstance()),
+  history: async ({ }, use) => await use(ChangeLogDataSource.getInstance()),
+  media: async ({ }, use) => await use(MutableMediaDataSource.getInstance()),
+  users: async ({ }, use) => await use(UserDataSource.getInstance()),
+  changeLog: async ({ }, use) => await use(ChangeLogDataSource.getInstance()),
+
+  waitForChanges: async ({ changeLog, task }, use) => {
+    const changeStream = changeLog.changeLogModel.collection.watch<ChangeLogType>()
+
+    async function wait (props: WaitProps): Promise<void> {
+      return await new Promise<void>((resolve) => {
+        const listener = changeStream.on('change', (doc) => {
+          let changes: BaseChangeRecordType[]
+
+          if (doc.operationType === 'insert') {
+            changes = doc.fullDocument.changes
+          } else if (doc.operationType === 'update') {
+            assert(doc.updateDescription.updatedFields?.changes)
+            changes = doc.updateDescription.updatedFields?.changes
+          } else {
+            // we may not know what to do here
+            return
+          }
+
+          if (changes[0] === undefined) return
+
+          if ((props.count === undefined && changes.length === 1) || changes.length === props.count) {
+            resolve()
+            listener.close()?.catch(logger.warn)
+          }
+        })
+      })
+    }
+
+    await use(wait)
+    await changeStream.close()
+  }
+})
+
+interface WaitProps {
+  count?: number
+  // operation?: AreaOperationType | ClimbEditOperationType
+  document: { _id: mongoose.Types.ObjectId | MUUID }
+}
diff --git a/src/__tests__/gradeUtils.ts b/src/__tests__/gradeUtils.ts
index 94c8fffe..7682e83d 100644
--- a/src/__tests__/gradeUtils.ts
+++ b/src/__tests__/gradeUtils.ts
@@ -42,7 +42,7 @@ describe('Test grade utilities', () => {
 
   it('creates grade object correctly in US context', () => {
     const context = gradeContextToGradeScales.US
-    if (context == null) fail('Bad grade context.  Should not happen.')
+    assert(context != null, 'Bad grade context, should not happen')
 
     let actual = createGradeObject('5.9', sanitizeDisciplines({ sport: true }), context)
     expect(actual).toEqual({
@@ -88,9 +88,9 @@ describe('Test grade utilities', () => {
     expect(actual).toBeUndefined()
   })
 
-  it.failing('can alpine ice grades to climbs with discipline ice', () => {
+  it.fails('can alpine ice grades to climbs with discipline ice', () => {
     const context = gradeContextToGradeScales.US
-    if (context == null) fail('Bad grade context.  Should not happen.')
+    assert(context != null, 'Bad grade context, should not happen')
 
     const actual = createGradeObject('AI2', sanitizeDisciplines({ ice: true }), context)
     expect(actual).toEqual({
@@ -100,7 +100,7 @@ describe('Test grade utilities', () => {
 
   it('creates grade object correctly in AU context', () => {
     const context = gradeContextToGradeScales.AU
-    if (context == null) fail('Bad grade context.  Should not happen.')
+    assert(context != null, 'Bad grade context, should not happen')
 
     let actual = createGradeObject('5', sanitizeDisciplines({ sport: true }), context)
     expect(actual).toEqual({
@@ -139,7 +139,7 @@ describe('Test grade utilities', () => {
 
   it('creates grade object correctly in FR context', () => {
     const context = gradeContextToGradeScales.FR
-    if (context == null) fail('Bad grade context.  Should not happen.')
+    assert(context != null, 'Bad grade context, should not happen')
 
     let actual = createGradeObject('5a', sanitizeDisciplines({ sport: true }), context)
     expect(actual).toEqual({
@@ -178,7 +178,8 @@ describe('Test grade utilities', () => {
 
   it('creates grade object correctly in BRZ context', () => {
     const context = gradeContextToGradeScales.BRZ
-    if (context == null) { fail('Bad grade context.  Should not happen.') }
+    assert(context != null, 'Bad grade context, should not happen')
+
     let actual = createGradeObject('V', sanitizeDisciplines({ sport: true }), context)
     expect(actual).toEqual({
       brazilian_crux: 'V'
diff --git a/src/__tests__/history.ts b/src/__tests__/history.ts
index 0c02f392..7dbd9196 100644
--- a/src/__tests__/history.ts
+++ b/src/__tests__/history.ts
@@ -1,50 +1,10 @@
-import { ApolloServer } from '@apollo/server'
-import muuid from 'uuid-mongodb'
-import { jest } from '@jest/globals'
-import MutableAreaDataSource from '../model/MutableAreaDataSource.js'
-import MutableOrganizationDataSource from '../model/MutableOrganizationDataSource.js'
-import MutableClimbDataSource from '../model/MutableClimbDataSource.js'
-import { AreaType } from '../db/AreaTypes.js'
-import { OrganizationType, OrgType } from '../db/OrganizationTypes.js'
+import mongoose from 'mongoose'
+import { OrgType } from '../db/OrganizationTypes.js'
 import { muuidToString } from '../utils/helpers.js'
-import { queryAPI, setUpServer } from '../utils/testUtils.js'
-import { InMemoryDB } from '../utils/inMemoryDB.js'
-import express from 'express'
-
-jest.setTimeout(60000)
+import { gqlTest as it } from './fixtures/gql.fixtures.js'
+import muuid from 'uuid-mongodb'
 
 describe('history API', () => {
-  let server: ApolloServer
-  let user: muuid.MUUID
-  let userUuid: string
-  let app: express.Application
-  let inMemoryDB: InMemoryDB
-
-  // Mongoose models for mocking pre-existing state.
-  let areas: MutableAreaDataSource
-  let organizations: MutableOrganizationDataSource
-  let climbs: MutableClimbDataSource
-
-  beforeAll(async () => {
-    ({ server, inMemoryDB, app } = await setUpServer())
-    // Auth0 serializes uuids in "relaxed" mode, resulting in this hex string format
-    // "59f1d95a-627d-4b8c-91b9-389c7424cb54" instead of base64 "WfHZWmJ9S4yRuTicdCTLVA==".
-    user = muuid.mode('relaxed').v4()
-    userUuid = muuidToString(user)
-  })
-
-  beforeEach(async () => {
-    await inMemoryDB.clear()
-    areas = MutableAreaDataSource.getInstance()
-    organizations = MutableOrganizationDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
-  })
-
-  afterAll(async () => {
-    await server.stop()
-    await inMemoryDB.close()
-  })
-
   describe('queries', () => {
     const FRAGMENT_CHANGE_HISTORY = `
       fragment ChangeHistoryFields on History {
@@ -89,39 +49,43 @@ describe('history API', () => {
       }
     `
 
-    let usa: AreaType
-    let ca: AreaType
-    let alphaOrg: OrganizationType
-    let climbIds: string[]
-
-    it('queries recent change history successfully', async () => {
+    it('queries recent change history successfully', async ({ user, userUuid, query, climbs, organizations, area, country }) => {
       // Make changes to be tracked.
-      usa = await areas.addCountry('usa')
-      ca = await areas.addArea(user, 'CA', usa.metadata.area_id)
       const alphaFields = {
         displayName: 'Alpha OpenBeta Club',
-        associatedAreaIds: [usa.metadata.area_id],
+        associatedAreaIds: [country.metadata.area_id],
         email: 'admin@alphaopenbeta.com'
       }
-      alphaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, alphaFields)
-      climbIds = await climbs.addOrUpdateClimbs(user, ca.metadata.area_id, [{ name: 'Alpha Climb' }])
+
+      const alphaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, alphaFields)
+      const climbIds = await climbs.addOrUpdateClimbs(user, area.metadata.area_id, [{ name: 'Alpha Climb' }])
 
       // Query for changes and ensure they are tracked.
-      const resp = await queryAPI({
+      const resp = await query({
         query: QUERY_RECENT_CHANGE_HISTORY,
         variables: { filter: {} },
-        userUuid,
-        app
+        userUuid
       })
+
       expect(resp.statusCode).toBe(200)
       const histories = resp.body.data.getChangeHistory
-      expect(histories.length).toBe(3)
 
-      // Latest change first
-      // Note: addCountry is not captured by history.
-      const [climbChange, orgChange, areaChange] = histories
+      await new Promise((resolve) => setTimeout(resolve, 500))
+      const climb = await climbs.findOneClimbByMUUID(muuid.from(climbIds[0]))
+
+      assert(climb)
+      assert(climb?._change?.historyId)
+      assert(area._change?.historyId)
+      assert(alphaOrg._change?.historyId)
+
+      const areaChange = histories.find(item => area._change?.historyId.equals(new mongoose.Types.ObjectId(item.id)))
+      const orgChange = histories.find(item => alphaOrg._change?.historyId.equals(new mongoose.Types.ObjectId(item.id)))
+      const climbChange = histories.find(item => climb?._change?.historyId.equals(new mongoose.Types.ObjectId(item.id)))
+
+      assert(climbChange)
+      assert(orgChange)
+      assert(areaChange)
 
-      expect(climbChange.operation).toBe('updateClimb')
       expect(climbChange.editedBy).toBe(userUuid)
 
       /**
@@ -135,7 +99,7 @@ describe('history API', () => {
       const insertChange = climbChange.changes.filter(c => c.dbOp === 'insert')[0]
       const updateChange = climbChange.changes.filter(c => c.dbOp === 'update')[0]
       expect(insertChange.fullDocument.uuid).toBe(climbIds[0])
-      expect(updateChange.fullDocument.uuid).toBe(muuidToString(ca.metadata.area_id))
+      expect(updateChange.fullDocument.uuid).toBe(muuidToString(area.metadata.area_id))
 
       expect(orgChange.operation).toBe('addOrganization')
       expect(orgChange.editedBy).toBe(userUuid)
diff --git a/src/__tests__/organizations.ts b/src/__tests__/organizations.ts
index f3dd931d..d6b8eb8c 100644
--- a/src/__tests__/organizations.ts
+++ b/src/__tests__/organizations.ts
@@ -1,58 +1,53 @@
-import { ApolloServer } from '@apollo/server'
-import muuid from 'uuid-mongodb'
-import MutableAreaDataSource from '../model/MutableAreaDataSource.js'
-import MutableOrganizationDataSource from '../model/MutableOrganizationDataSource.js'
-import { AreaType } from '../db/AreaTypes.js'
 import { OperationType, OrganizationEditableFieldsType, OrganizationType, OrgType } from '../db/OrganizationTypes.js'
 import ChangeLogDataSource from '../model/ChangeLogDataSource.js'
-import { queryAPI, setUpServer } from '../utils/testUtils.js'
 import { muuidToString } from '../utils/helpers.js'
 import { validate as validateMuuid } from 'uuid'
-import { InMemoryDB } from '../utils/inMemoryDB.js'
-import express from 'express'
-
-describe('organizations API', () => {
-  let server: ApolloServer
-  let user: muuid.MUUID
-  let userUuid: string
-  let app: express.Application
-  let inMemoryDB: InMemoryDB
-
-  // Mongoose models for mocking pre-existing state.
-  let areas: MutableAreaDataSource
-  let organizations: MutableOrganizationDataSource
-  let usa: AreaType
-  let ca: AreaType
-  let wa: AreaType
-
-  beforeAll(async () => {
-    ({ server, inMemoryDB, app } = await setUpServer())
-    // Auth0 serializes uuids in "relaxed" mode, resulting in this hex string format
-    // "59f1d95a-627d-4b8c-91b9-389c7424cb54" instead of base64 "WfHZWmJ9S4yRuTicdCTLVA==".
-    user = muuid.mode('relaxed').v4()
-    userUuid = muuidToString(user)
-  })
+import { gqlTest } from './fixtures/gql.fixtures'
+import { AreaType } from '../db/AreaTypes.js'
 
-  beforeEach(async () => {
-    await inMemoryDB.clear()
-    areas = MutableAreaDataSource.getInstance()
-    organizations = MutableOrganizationDataSource.getInstance()
-    usa = await areas.addCountry('usa')
-    ca = await areas.addArea(user, 'CA', usa.metadata.area_id)
-    wa = await areas.addArea(user, 'WA', usa.metadata.area_id)
-  })
+interface LocalContext {
+  ca: AreaType
+  wa: AreaType
+  orgData: OrganizationEditableFieldsType[]
+  orgs: OrganizationType[]
+}
 
-  afterAll(async () => {
-    await server?.stop()
-    await inMemoryDB?.close()
-  })
+const it = gqlTest.extend<LocalContext >({
+  ca: async ({ addArea }, use) => await use(await addArea()),
+  wa: async ({ addArea }, use) => await use(await addArea()),
+  orgData: async ({ wa, ca, task }, use) => {
+    await use([
+      {
+        displayName: `${task.id} Alpha OpenBeta Club`,
+        associatedAreaIds: [ca.metadata.area_id, wa.metadata.area_id],
+        email: 'admin@alphaopenbeta.com',
+        facebookLink: 'https://www.facebook.com/alphaopenbeta',
+        instagramLink: 'https://www.instagram.com/alphaopenbeta',
+        hardwareReportLink: 'https://alphaopenbeta.com/reporthardware'
+      },
+      {
+        displayName: `${task.id} Delta OpenBeta Club`,
+        email: 'admin@deltaopenbeta.com'
+      },
+      {
+        displayName: `${task.id}Delta Gamma OpenBeta Club`,
+        description: 'We are an offshoot of the delta club.\nSee our website for more details.',
+        excludedAreaIds: [wa.metadata.area_id]
+      }
+    ])
+  },
+  orgs: async ({ organizations, user, orgData }, use) => {
+    await use(await Promise.all(orgData.map(async fields => await organizations.addOrganization(user, OrgType.localClimbingOrganization, fields))))
+  }
+})
 
+describe('organizations API', () => {
   describe('mutations', () => {
     const createQuery = `
       mutation addOrganization($input: AddOrganizationInput!) {
         organization: addOrganization(input: $input) {
           orgId
-          orgType 
+          orgType
           displayName
           associatedAreaIds
           excludedAreaIds
@@ -81,14 +76,14 @@ describe('organizations API', () => {
       }
     `
 
-    it('creates and updates an organization', async () => {
-      const createResponse = await queryAPI({
+    it('creates and updates an organization', async ({ query, userUuid, country, addArea }) => {
+      const areaToExclude = await addArea()
+      const createResponse = await query({
         query: createQuery,
         operationName: 'addOrganization',
         variables: { input: { displayName: 'Friends of Openbeta', orgType: 'LOCAL_CLIMBING_ORGANIZATION' } },
         userUuid,
-        roles: ['user_admin'],
-        app
+        roles: ['user_admin']
       })
 
       expect(createResponse.statusCode).toBe(200)
@@ -102,14 +97,14 @@ describe('organizations API', () => {
       expect(createResponse.body.data.organization.createdBy).toBe(userUuid)
       expect(createResponse.body.data.organization.updatedBy).toBe(userUuid)
 
-      const updateResponse = await queryAPI({
+      const updateResponse = await query({
         query: updateQuery,
         operationName: 'updateOrganization',
         variables: {
           input: {
             orgId,
-            associatedAreaIds: [muuidToString(usa.metadata.area_id)],
-            excludedAreaIds: [muuidToString(wa.metadata.area_id)],
+            associatedAreaIds: [muuidToString(country.metadata.area_id)],
+            excludedAreaIds: [muuidToString(areaToExclude.metadata.area_id)],
             displayName: 'Allies of Openbeta',
             website: 'https://alliesofopenbeta.com',
             email: 'admin@alliesofopenbeta.com',
@@ -121,15 +116,14 @@ describe('organizations API', () => {
           }
         },
         userUuid,
-        roles: ['user_admin'],
-        app
+        roles: ['user_admin']
       })
       expect(updateResponse.statusCode).toBe(200)
       expect(updateResponse.body.errors).toBeUndefined()
       const orgResult = updateResponse.body.data.organization
       expect(orgResult.orgId).toBe(orgId)
-      expect(orgResult.associatedAreaIds).toEqual([muuidToString(usa.metadata.area_id)])
-      expect(orgResult.excludedAreaIds).toEqual([muuidToString(wa.metadata.area_id)])
+      expect(orgResult.associatedAreaIds).toEqual([muuidToString(country.metadata.area_id)])
+      expect(orgResult.excludedAreaIds).toEqual([muuidToString(areaToExclude.metadata.area_id)])
       expect(orgResult.displayName).toBe('Allies of Openbeta')
       expect(orgResult.content.website).toBe('https://alliesofopenbeta.com')
       expect(orgResult.content.email).toBe('admin@alliesofopenbeta.com')
@@ -161,14 +155,13 @@ describe('organizations API', () => {
       expect(createRecord[0].fullDocument.displayName).toBe('Friends of Openbeta')
     })
 
-    it('throws an error if a non-user_admin tries to add an organization', async () => {
-      const response = await queryAPI({
+    it('throws an error if a non-user_admin tries to add an organization', async ({ query, userUuid }) => {
+      const response = await query({
         query: createQuery,
         operationName: 'addOrganization',
         variables: { input: { displayName: 'Friends of Openbeta', orgType: 'LOCAL_CLIMBING_ORGANIZATION' } },
         userUuid,
-        roles: ['editor'],
-        app
+        roles: ['editor']
       })
       expect(response.statusCode).toBe(200)
       expect(response.body.data.organization).toBeNull()
@@ -205,140 +198,92 @@ describe('organizations API', () => {
         }
       }
     `
-    let alphaFields: OrganizationEditableFieldsType
-    let deltaFields: OrganizationEditableFieldsType
-    let gammaFields: OrganizationEditableFieldsType
-    let alphaOrg: OrganizationType
-    let deltaOrg: OrganizationType
-    let gammaOrg: OrganizationType
-
-    beforeEach(async () => {
-      alphaFields = {
-        displayName: 'Alpha OpenBeta Club',
-        associatedAreaIds: [ca.metadata.area_id, wa.metadata.area_id],
-        email: 'admin@alphaopenbeta.com',
-        facebookLink: 'https://www.facebook.com/alphaopenbeta',
-        instagramLink: 'https://www.instagram.com/alphaopenbeta',
-        hardwareReportLink: 'https://alphaopenbeta.com/reporthardware'
-      }
-      alphaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, alphaFields)
-        .then((res: OrganizationType | null) => {
-          if (res === null) throw new Error('Failure mocking organization.')
-          return res
-        })
 
-      deltaFields = {
-        displayName: 'Delta OpenBeta Club',
-        email: 'admin@deltaopenbeta.com'
-      }
-      deltaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, deltaFields)
-        .then((res: OrganizationType | null) => {
-          if (res === null) throw new Error('Failure mocking organization.')
-          return res
-        })
-
-      gammaFields = {
-        displayName: 'Delta Gamma OpenBeta Club',
-        description: 'We are an offshoot of the delta club.\nSee our website for more details.',
-        excludedAreaIds: [wa.metadata.area_id]
-      }
-      gammaOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, gammaFields)
-        .then((res: OrganizationType | null) => {
-          if (res === null) throw new Error('Failure mocking organization.')
-          return res
-        })
-    })
-
-    it('retrieves an organization with an MUUID', async () => {
-      const response = await queryAPI({
+    it('retrieves an organization with an MUUID', async ({ query, userUuid, orgs, orgData, ca, wa }) => {
+      const response = await query({
         query: organizationQuery,
         operationName: 'organization',
-        variables: { input: muuidToString(alphaOrg.orgId) },
-        userUuid,
-        app
+        variables: { input: muuidToString(orgs[0].orgId) },
+        userUuid
       })
       expect(response.statusCode).toBe(200)
       const orgResult = response.body.data.organization
-      expect(orgResult.orgId).toBe(muuidToString(alphaOrg.orgId))
-      expect(orgResult.displayName).toBe(alphaFields.displayName)
+      expect(orgResult.orgId).toBe(muuidToString(orgs[0].orgId))
+      expect(orgResult.displayName).toBe(orgs[0].displayName)
       expect(orgResult.associatedAreaIds.sort()).toEqual([muuidToString(ca.metadata.area_id), muuidToString(wa.metadata.area_id)].sort())
-      expect(orgResult.content.email).toBe(alphaFields.email)
-      expect(orgResult.content.instagramLink).toBe(alphaFields.instagramLink)
-      expect(orgResult.content.facebookLink).toBe(alphaFields.facebookLink)
-      expect(orgResult.content.hardwareReportLink).toBe(alphaFields.hardwareReportLink)
+      expect(orgResult.content.email).toBe(orgData[0].email)
+      expect(orgResult.content.instagramLink).toBe(orgData[0].instagramLink)
+      expect(orgResult.content.facebookLink).toBe(orgData[0].facebookLink)
+      expect(orgResult.content.hardwareReportLink).toBe(orgData[0].hardwareReportLink)
     })
 
-    it('retrieves organizations using an exactMatch displayName filter', async () => {
-      const response = await queryAPI({
+    it('retrieves organizations using an exactMatch displayName filter', async ({ query, userUuid, orgs }) => {
+      const response = await query({
         query: organizationsQuery,
         operationName: 'organizations',
-        variables: { filter: { displayName: { match: 'Delta OpenBeta Club', exactMatch: true } } },
-        userUuid,
-        app
+        variables: { filter: { displayName: { match: orgs[1].displayName, exactMatch: true } } },
+        userUuid
       })
 
       expect(response.statusCode).toBe(200)
       const dataResult = response.body.data.organizations
       expect(dataResult.length).toBe(1)
-      expect(dataResult[0].orgId).toBe(muuidToString(deltaOrg.orgId))
+      expect(dataResult[0].orgId).toBe(muuidToString(orgs[1].orgId))
     })
 
-    it('retrieves organizations using a non-exactMatch displayName filter', async () => {
-      const response = await queryAPI({
+    it('retrieves organizations using a non-exactMatch displayName filter', async ({ query, userUuid, orgs, task }) => {
+      const response = await query({
         query: organizationsQuery,
         operationName: 'organizations',
-        variables: { filter: { displayName: { match: 'delta', exactMatch: false } } },
-        userUuid,
-        app
+        variables: { filter: { displayName: { match: task.id, exactMatch: false } } },
+        userUuid
       })
       expect(response.statusCode).toBe(200)
       const dataResult = response.body.data.organizations
-      expect(dataResult.length).toBe(2)
-      expect(dataResult.map(o => o.orgId).sort()).toEqual([muuidToString(deltaOrg.orgId), muuidToString(gammaOrg.orgId)].sort())
+      expect(dataResult.map(o => o.orgId).sort())
+        .toEqual([muuidToString(orgs[0].orgId), muuidToString(orgs[1].orgId), muuidToString(orgs[2].orgId)].sort())
     })
 
-    it('limits organizations returned', async () => {
-      const response = await queryAPI({
+    it('limits organizations returned', async ({ userUuid, query }) => {
+      const response = await query({
         query: organizationsQuery,
         operationName: 'organizations',
         variables: {
           limit: 1
         },
-        userUuid,
-        app
+        userUuid
       })
       expect(response.statusCode).toBe(200)
       const dataResult = response.body.data.organizations
       expect(dataResult.length).toBe(1) // Three matching orgs, but only return one.
     })
 
-    it('retrieves organizations using an associatedAreaIds filter', async () => {
-      const response = await queryAPI({
+    it('retrieves organizations using an associatedAreaIds filter', async ({ userUuid, query, ca, orgs, wa }) => {
+      const response = await query({
         query: organizationsQuery,
         operationName: 'organizations',
         variables: { filter: { associatedAreaIds: { includes: [muuidToString(ca.metadata.area_id)] } } },
-        userUuid,
-        app
+        userUuid
       })
+
       // Graphql should convert `includes` from a string[] to MUUID[]
       expect(response.statusCode).toBe(200)
       const dataResult = response.body.data.organizations
       expect(dataResult.length).toBe(1)
-      expect(dataResult[0].orgId).toBe(muuidToString(alphaOrg.orgId))
+      expect(dataResult[0].orgId).toBe(muuidToString(orgs[0].orgId))
     })
 
-    it('excludes organizations using an excludedAreaIds filter', async () => {
-      const response = await queryAPI({
+    it('excludes organizations using an excludedAreaIds filter', async ({ userUuid, query, wa, orgs }) => {
+      const response = await query({
         query: organizationsQuery,
         operationName: 'organizations',
         variables: { filter: { excludedAreaIds: { excludes: [muuidToString(wa.metadata.area_id)] } } },
-        userUuid,
-        app
+        userUuid
       })
       expect(response.statusCode).toBe(200)
       const dataResult = response.body.data.organizations
-      expect(dataResult.length).toBe(2)
-      expect(dataResult.map((o: OrganizationType) => o.orgId).includes(muuidToString(gammaOrg.orgId))).toBeFalsy()
+      // We want the org that explicitly excludes wa to be absent from this array.
+      expect(dataResult.map((o: OrganizationType) => o.orgId).includes(muuidToString(orgs[2].orgId))).toBeFalsy()
     })
   })
 })
diff --git a/src/__tests__/ticks.ts b/src/__tests__/ticks.ts
index 224fb8b8..a1a86fdb 100644
--- a/src/__tests__/ticks.ts
+++ b/src/__tests__/ticks.ts
@@ -1,94 +1,36 @@
-import { ApolloServer } from '@apollo/server'
-import muuid from 'uuid-mongodb'
-import { jest } from '@jest/globals'
-import { queryAPI, setUpServer } from '../utils/testUtils.js'
+import gql from 'graphql-tag'
+import { TickInput, TickType } from '../db/TickTypes.js'
 import { muuidToString } from '../utils/helpers.js'
-import { TickInput } from '../db/TickTypes.js'
-import TickDataSource from '../model/TickDataSource.js'
-import UserDataSource from '../model/UserDataSource.js'
-import { UpdateProfileGQLInput } from '../db/UserTypes.js'
-import { InMemoryDB } from '../utils/inMemoryDB.js'
-import express from 'express'
-import MutableClimbDataSource from '../model/MutableClimbDataSource.js'
-import MutableAreaDataSource from '../model/MutableAreaDataSource.js'
-import { ClimbChangeInputType } from '../db/ClimbTypes.js'
+import { allowableStyleMap, choose } from './fixtures/data.fixtures.js'
+import { gqlTest } from './fixtures/gql.fixtures.js'
 
-jest.setTimeout(110000)
+interface LocalContext {
+  singleTickData: TickInput
+  tick: TickType
+}
 
-const newClimbsToAdd: ClimbChangeInputType[] = [
-  {
-    name: 'Sport 1',
-    disciplines: {
-      sport: true
-    },
-    description: 'The best climb',
-    location: '5m left of the big tree',
-    protection: '5 quickdraws'
-  },
-  {
-    name: 'Deep water 1',
-    disciplines: {
-      deepwatersolo: true
-    }
-  }
-]
-
-describe('ticks API', () => {
-  let server: ApolloServer
-  let user: muuid.MUUID
-  let userUuid: string
-  let app: express.Application
-  let inMemoryDB: InMemoryDB
-
-  // Mongoose models for mocking pre-existing state.
-  let ticks: TickDataSource
-  let users: UserDataSource
-  let tickOne: TickInput
-  let climbs: MutableClimbDataSource
-  let areas: MutableAreaDataSource
-
-  beforeAll(async () => {
-    ({ server, inMemoryDB, app } = await setUpServer())
-    user = muuid.v4()
-    userUuid = muuidToString(user)
-
-    tickOne = {
+const it = gqlTest.extend<LocalContext>({
+  singleTickData: async ({ userUuid, climb }, use) => {
+    await use({
       name: 'Route One',
       notes: 'Nice slab',
-      climbId: 'tbd', // need to create a climb for tick validation
+      climbId: muuidToString(climb._id),
       userId: userUuid,
       style: 'Lead',
-      attemptType: 'Onsight',
+      attemptType: choose(allowableStyleMap.Lead),
       dateClimbed: new Date('2016-07-20T17:30:15+05:30'),
       grade: '5.8',
       source: 'MP'
-    }
-  })
-
-  beforeEach(async () => {
-    ticks = TickDataSource.getInstance()
-    users = UserDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
-    areas = MutableAreaDataSource.getInstance()
-    await inMemoryDB.clear()
-
-    // Add climbs because add/update tick requires type validation
-    await areas.addCountry('usa')
-    const newDestination = await areas.addArea(user, 'California', null, 'usa')
-    const routesArea = await areas.addArea(user, 'Sport & Trad', newDestination.metadata.area_id)
-
-    const newIDs = await climbs.addOrUpdateClimbs(user, routesArea.metadata.area_id, newClimbsToAdd)
-    // Update tick inputs with generated climb IDs
-    tickOne.climbId = newIDs[0]
-  })
-
-  afterAll(async () => {
-    await server.stop()
-    await inMemoryDB.close()
-  })
+    })
+  },
+  tick: async ({ ticks, singleTickData }, use) => {
+    await use(await ticks.addTick(singleTickData))
+  }
+})
 
+describe('ticks API', () => {
   describe('queries', () => {
-    const userQuery = `
+    const userQuery = gql`
       query userTicks($userId: MUUID, $username: String) {
         userTicks(userId: $userId, username: $username) {
           _id
@@ -103,7 +45,7 @@ describe('ticks API', () => {
         }
       }
     `
-    const userTickByClimbQuery = `
+    const userTickByClimbQuery = gql`
       query userTicksByClimbId($userId: String, $climbId: String) {
         userTicksByClimbId(userId: $userId, climbId: $climbId) {
           _id
@@ -119,58 +61,40 @@ describe('ticks API', () => {
       }
     `
 
-    it('queries by userId', async () => {
-      const userProfileInput: UpdateProfileGQLInput = {
-        userUuid,
-        username: 'cat.dog',
-        email: 'cat@example.com'
-      }
-      await users.createOrUpdateUserProfile(user, userProfileInput)
-      await ticks.addTick(tickOne)
-      const response = await queryAPI({
+    it('queries by userId', async ({ userUuid, profile, tick, query }) => {
+      const response = await query({
         query: userQuery,
-        variables: { userId: userUuid },
-        userUuid,
-        app
+        variables: { userId: muuidToString(profile._id) }
       })
+
       expect(response.statusCode).toBe(200)
       const res = response.body.data.userTicks
       expect(res).toHaveLength(1)
-      expect(res[0].name).toBe(tickOne.name)
+      expect(res[0].name).toBe(tick.name)
     })
 
-    it('queries by username', async () => {
-      const userProfileInput: UpdateProfileGQLInput = {
-        userUuid,
-        username: 'cat.dog',
-        email: 'cat@example.com'
-      }
-      await users.createOrUpdateUserProfile(user, userProfileInput)
-      await ticks.addTick(tickOne)
-      const response = await queryAPI({
+    it('queries by username', async ({ userUuid, profile, tick, query }) => {
+      const response = await query({
         query: userQuery,
-        variables: { username: 'cat.dog' },
-        userUuid,
-        app
+        variables: { username: profile.username },
+        userUuid
       })
-      expect(response.statusCode).toBe(200)
+      expect(response.statusCode, JSON.stringify(response.body.errors)).toBe(200)
       const res = response.body.data.userTicks
       expect(res).toHaveLength(1)
-      expect(res[0].name).toBe(tickOne.name)
+      expect(res[0].name).toBe(tick.name)
     })
 
-    it('queries by userId and climbId', async () => {
-      await ticks.addTick(tickOne)
-      const response = await queryAPI({
+    it('queries by userId and climbId', async ({ tick, query, userUuid }) => {
+      const response = await query({
         query: userTickByClimbQuery,
-        variables: { userId: userUuid, climbId: tickOne.climbId },
-        userUuid,
-        app
+        variables: { userId: userUuid, climbId: tick.climbId },
+        userUuid
       })
       expect(response.statusCode).toBe(200)
       const res = response.body.data.userTicksByClimbId
       expect(res).toHaveLength(1)
-      expect(res[0].name).toBe(tickOne.name)
+      expect(res[0].name).toBe(tick.name)
     })
   })
 
@@ -207,36 +131,47 @@ describe('ticks API', () => {
         }
       }
     `
-    it('creates and updates a tick', async () => {
-      const createResponse = await queryAPI({
+
+    it('creates and updates a tick', async ({
+      query,
+      userUuid,
+      singleTickData
+    }) => {
+      const createResponse = await query({
         query: createQuery,
-        variables: { input: tickOne },
+        variables: { input: singleTickData },
         userUuid,
-        roles: ['user_admin'],
-        app
+        roles: ['user_admin']
       })
 
       expect(createResponse.statusCode).toBe(200)
+      expect(createResponse.body).toBeTruthy()
+      expect(createResponse.body.data).toBeTruthy()
+      expect(createResponse.body.data.tick).toBeTruthy()
+
       const createTickRes = createResponse.body.data.tick
-      expect(createTickRes.name).toBe(tickOne.name)
-      expect(createTickRes.notes).toBe(tickOne.notes)
-      expect(createTickRes.climbId).toBe(tickOne.climbId)
-      expect(createTickRes.userId).toBe(tickOne.userId)
-      expect(createTickRes.style).toBe(tickOne.style)
-      expect(createTickRes.attemptType).toBe(tickOne.attemptType)
-      expect(createTickRes.dateClimbed).toBe(new Date(tickOne.dateClimbed).getTime())
-      expect(createTickRes.grade).toBe(tickOne.grade)
-      expect(createTickRes.source).toBe(tickOne.source)
+
+      expect(createTickRes.name).toBe(singleTickData.name)
+      expect(createTickRes.notes).toBe(singleTickData.notes)
+      expect(createTickRes.climbId).toBe(singleTickData.climbId)
+      expect(createTickRes.userId).toBe(singleTickData.userId)
+      expect(createTickRes.style).toBe(singleTickData.style)
+      expect(createTickRes.attemptType).toBe(singleTickData.attemptType)
+      expect(createTickRes.dateClimbed).toBe(
+        new Date(singleTickData.dateClimbed).getTime()
+      )
+      expect(createTickRes.grade).toBe(singleTickData.grade)
+      expect(createTickRes.source).toBe(singleTickData.source)
       expect(createTickRes._id).toBeTruthy()
 
-      const updateResponse = await queryAPI({
+      const updateResponse = await query({
         query: updateQuery,
         variables: {
           input: {
             _id: createTickRes._id,
             updatedTick: {
               name: 'Updated Route One',
-              climbId: tickOne.climbId,
+              climbId: singleTickData.climbId,
               userId: userUuid,
               dateClimbed: new Date('2022-11-10T12:00:00Z'),
               grade: 'new grade',
@@ -245,28 +180,32 @@ describe('ticks API', () => {
           }
         },
         userUuid,
-        roles: [], // ['user_admin'],
-        app
+        roles: []
       })
 
       expect(updateResponse.statusCode).toBe(200)
       expect(updateResponse.body.data.tick.name).toBe('Updated Route One')
     })
-    it('verifies date formats correctly', async () => {
+
+    it('verifies date formats correctly', async ({
+      singleTickData,
+      query,
+      userUuid
+    }) => {
       const validDateTick = {
-        ...tickOne,
+        ...singleTickData,
         dateClimbed: new Date('2022-11-10T15:30:00Z').getTime()
       }
-      const validResponse = await queryAPI({
+      const validResponse = await query({
         query: createQuery,
         variables: { input: validDateTick },
         userUuid,
-        roles: ['user_admin'],
-        app
+        roles: ['user_admin']
       })
       expect(validResponse.statusCode).toBe(200)
-      expect(validResponse.body.data.tick.dateClimbed)
-        .toBe(new Date('2022-11-10T15:30:00Z').getTime())
+      expect(validResponse.body.data.tick.dateClimbed).toBe(
+        new Date('2022-11-10T15:30:00Z').getTime()
+      )
     })
   })
 })
diff --git a/src/db/export/json/async-file.processor.test.ts b/src/db/export/json/async-file.processor.test.ts
index c02ddf19..8d9491ef 100644
--- a/src/db/export/json/async-file.processor.test.ts
+++ b/src/db/export/json/async-file.processor.test.ts
@@ -1,10 +1,11 @@
+import { logger } from '../../../logger'
 import { asyncFileProcessor, Writer } from './async-file.processor'
 import path from 'path'
 
 interface TestType { name: string, path?: string[] }
 
 describe('file processor', () => {
-  const writer = jest.fn(async (_data, _path) => await Promise.resolve())
+  const writer = vi.fn(async (_data, _path) => await Promise.resolve())
   const testData: TestType[] = [{ name: 'test', path: ['one', 'two'] }, { name: 'test2' }]
   const testPath = 'testPath'
 
@@ -21,11 +22,13 @@ describe('file processor', () => {
     })
   }
 
-  function withFailedWriteOn (failingData: { name: string }) {
+  function withFailedWriteOn (failingData: { name: string }): Writer {
     return async (data, path) => {
+      logger.info(data, failingData)
       if (data === JSON.stringify(failingData)) {
         return await Promise.reject('error')
       }
+
       return await writer(data, path)
     }
   }
@@ -42,7 +45,10 @@ describe('file processor', () => {
   it('should continue batch processing on error', async () => {
     const processor = createProcessor(withFailedWriteOn(testData[0]))
 
-    await expect(processor(testData, 0)).rejects.toContain('Failed to write 1/2 files')
+    // First, check that our failed writer fires as expected
+    await expect(() => withFailedWriteOn(testData[0])(JSON.stringify(testData[0]), 'path')).rejects.toContain('error')
+    // now in the context of a strem, we should expect 1 out of two possible files to fail
+    await expect(async () => await processor(testData, 0)).rejects.toThrow('Failed to write 1/2 files')
 
     assertWriterCalledFor(testData[1])
   })
diff --git a/src/db/import/usa/__tests__/Tree.test.ts b/src/db/import/usa/__tests__/Tree.test.ts
index ebb61a16..2640de90 100644
--- a/src/db/import/usa/__tests__/Tree.test.ts
+++ b/src/db/import/usa/__tests__/Tree.test.ts
@@ -1,3 +1,4 @@
+import { logger } from '../../../../logger'
 import { Tree, createRootNode } from '../AreaTree'
 
 const path1 = 'Oregon|Central Oregon|Paulina Peak|Vigilantes de Obsidiana|Roca Rhodales'
@@ -45,7 +46,7 @@ describe('Area Tree data structure', () => {
     const leaf = tree.atPath(path1)
     if (leaf !== undefined) {
       const ancestors = leaf.getAncestors()
-      console.log(ancestors)
+      logger.debug(ancestors)
       expect(ancestors.length).toEqual(path1.split('|').length + 1) // all element of path1 + 1 for US root
       expect(ancestors[0]).toEqual(countryRoot?.uuid)
       const stateRoot = tree.atPath('Oregon')
diff --git a/src/db/index.ts b/src/db/index.ts
index e31ae385..c5af1a40 100644
--- a/src/db/index.ts
+++ b/src/db/index.ts
@@ -81,7 +81,7 @@ export const gracefulExit = async (exitCode: number = 0): Promise<void> => {
 }
 
 export const defaultPostConnect = async (changeStreamListener = streamListener): Promise<ChangeStream> => {
-  console.log('Kudos!')
+  logger.debug('defaultPostConnect- Kudos!')
   await createIndexes()
   return await changeStreamListener()
 }
diff --git a/src/db/utils/__tests__/Aggregate.test.ts b/src/db/utils/__tests__/Aggregate.test.ts
index f4ea7418..693b7bad 100644
--- a/src/db/utils/__tests__/Aggregate.test.ts
+++ b/src/db/utils/__tests__/Aggregate.test.ts
@@ -1,4 +1,3 @@
-import { jest } from '@jest/globals'
 import { logger } from '../../../logger'
 import { aggregateCragStats, merge } from '../Aggregate'
 import { AggregateType } from '../../AreaTypes'
@@ -40,7 +39,7 @@ describe('Aggregate merge', () => {
 
 describe('Aggregate Crag Stats', () => {
   it('Provides crag stat aggregates in US grade context', () => {
-    jest.spyOn(logger, 'warn').mockImplementation(() => {})
+    vi.spyOn(logger, 'warn').mockImplementation(() => {})
     const crag = {
       gradeContext: 'US',
       climbs: [
diff --git a/src/logger.ts b/src/logger.ts
index e5694869..1fcd820a 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -13,5 +13,5 @@ const setupLogFlare = (apiKey?: string, sourceToken?: string): any | undefined =
 
 export const logger = pino({
   name: 'openbeta-graphql',
-  level: 'info'
+  level: process.env.LOG_LEVEL ?? 'info'
 }, setupLogFlare(process.env.LOGFLARE_API_KEY, process.env.LOGFLARE_SOURCE_TOKEN))
diff --git a/src/model/MutableAreaDataSource.ts b/src/model/MutableAreaDataSource.ts
index 0ba2dec2..3a7d0248 100644
--- a/src/model/MutableAreaDataSource.ts
+++ b/src/model/MutableAreaDataSource.ts
@@ -147,6 +147,7 @@ export default class MutableAreaDataSource extends AreaDataSource {
     } else {
       // account for a few new/unofficial countries without lat,lng in the lookup table
       logger.warn(`Missing lnglat for ${countryName}`)
+      throw Error(`Missing lnglat for ${countryName}`)
     }
 
     await this.validateUniqueAreaName(countryName, null)
diff --git a/src/model/__tests__/AreaHistoryDataSource.ts b/src/model/__tests__/AreaHistoryDataSource.ts
index 424490c4..5b49834c 100644
--- a/src/model/__tests__/AreaHistoryDataSource.ts
+++ b/src/model/__tests__/AreaHistoryDataSource.ts
@@ -1,169 +1,208 @@
 import muuid from 'uuid-mongodb'
-
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-import ChangeLogDataSource from '../ChangeLogDataSource.js'
-import { OperationType } from '../../db/AreaTypes.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
-import waitForExpect from 'wait-for-expect'
-import jest from 'jest-mock'
+import { dataFixtures as it } from '../../__tests__/fixtures/data.fixtures.js'
+import { AreaType } from '../../db/AreaTypes.js'
+import { BaseChangeRecordType } from '../../db/ChangeLogType.js'
 
 describe('Area history', () => {
-  let areas: MutableAreaDataSource
-  let onChange: jest.Mock
-  const testUser = muuid.v4()
-
-  beforeAll(async () => {
-    onChange = jest.fn()
-    await inMemoryDB.connect(onChange)
-    await ChangeLogDataSource.getInstance()._testRemoveAll()
-
-    areas = MutableAreaDataSource.getInstance()
-  })
-
-  afterAll(async () => {
-    try {
-      await inMemoryDB.close()
-    } catch (e) {
-      console.log('closing mongoose', e)
-    }
+  it('should create history changes for an area when children get added to it', async ({
+    changeLog,
+    area,
+    addArea,
+    waitForChanges
+  }) => {
+    const historySettled = waitForChanges({ document: area, count: 2 })
+    await addArea('nevada', { parent: area })
+    await addArea('oregon', { parent: area })
+    await historySettled
+
+    expect(
+      await changeLog.getAreaChangeSets(area.metadata.area_id)
+    ).toHaveLength(2)
   })
 
-  beforeEach(async () => {
-    await ChangeLogDataSource.getInstance()._testRemoveAll()
-    onChange.mockClear()
+  it('should properly seperate unrelated histories', async ({
+    changeLog,
+    area,
+    addArea,
+    waitForChanges
+  }) => {
+    const mainAreaHistory = waitForChanges({ document: area, count: 2 })
+    await Promise.all([
+      addArea(undefined, { parent: area }),
+      addArea(undefined, { parent: area })
+    ])
+    await mainAreaHistory
+
+    const randomHistory = await changeLog.getAreaChangeSets(muuid.v4())
+    expect(randomHistory).toHaveLength(0)
   })
 
-  it('should create history records for new subareas', async () => {
-    const usa = await areas.addCountry('usa')
-    const newArea = await areas.findOneAreaByUUID(usa.metadata.area_id)
-    expect(newArea.area_name).toEqual(usa.area_name)
+  it('should return change sets in most recent order', async ({
+    changeLog,
+    area,
+    addArea,
+    areas,
+    waitForChanges,
+    user
+  }) => {
+    const mainAreaHistory = waitForChanges({ document: area, count: 2 })
+    const child = await addArea(undefined, { parent: area })
+    await areas.deleteArea(user, child.metadata.area_id)
 
-    const or = await areas.addArea(testUser, 'oregon', usa.metadata.area_id)
-    const nv = await areas.addArea(testUser, 'nevada', usa.metadata.area_id)
+    await mainAreaHistory
 
-    expect(nv?._id).toBeTruthy()
-    expect(or?._id).toBeTruthy()
+    const changeSets = await changeLog.getAreaChangeSets(area.metadata.area_id)
 
-    await waitForExpect(() => expect(onChange).toHaveBeenCalledTimes(5))
-    const areaHistory = await ChangeLogDataSource.getInstance().getAreaChangeSets()
-
-    expect(areaHistory).toHaveLength(2)
     // verify changes in most recent order
-    expect(areaHistory[0].operation).toEqual(OperationType.addArea)
-    expect(areaHistory[1].operation).toEqual(OperationType.addArea)
-
-    // Verify NV history
-    const nvAreaHistory = areaHistory[0].changes
-    expect(nvAreaHistory).toHaveLength(2)
+    assert(area._change?.historyId)
+    assert(changeSets[1].changes[0].fullDocument._change?.historyId)
+    expect(
+      changeSets[0].changes[0].fullDocument._change?.prevHistoryId?.equals(
+        changeSets[1].changes[0].fullDocument._change?.historyId
+      )
+    )
+  })
 
-    // history is shown most recent first
-    expect(nvAreaHistory[0].dbOp).toEqual('insert') // insert new area
-    expect(nvAreaHistory[0].fullDocument.area_name).toEqual(nv?.area_name) // area added to the right parent?
+  it('should create history records for new subareas', async ({
+    changeLog,
+    area,
+    addArea,
+    country,
+    waitForChanges
+  }) => {
+    const mainAreaHistory = waitForChanges({ document: area, count: 2 })
+    const nv = await addArea('nevada', { parent: area })
+    await addArea('oregon', { parent: area })
+
+    await mainAreaHistory
+
+    const initialHistory = await changeLog.getAreaChangeSets(
+      area.metadata.area_id
+    )
+    const nvAreaHistory: Array<BaseChangeRecordType<AreaType>> =
+      initialHistory[1].changes
 
     // verify change history linking
-    expect(nvAreaHistory[0].fullDocument._change?.historyId).toEqual(areaHistory[0]._id) // should point to current change
-    expect(nvAreaHistory[0].fullDocument._change?.prevHistoryId).not.toBeDefined() // new document -> no previous history
+    expect(
+      nvAreaHistory[0].fullDocument._change?.historyId.equals(
+        initialHistory[0]._id
+      )
+    ) // should point to current change
+    expect(
+      nvAreaHistory[0].fullDocument._change?.prevHistoryId
+    ).not.toBeDefined() // new document -> no previous history
 
     expect(nvAreaHistory[1].dbOp).toEqual('update') // add area to country.children[]
-    expect(nvAreaHistory[1].fullDocument.area_name).toEqual(usa?.area_name)
+    expect(nvAreaHistory[1].fullDocument.area_name).toEqual(area?.area_name)
 
+    // coco: What? I don't see where this is supposed to happen I am confused
     expect(nvAreaHistory[1].fullDocument.children).toHaveLength(2)
     expect(nvAreaHistory[1].fullDocument.children[1]).toEqual(nv?._id) // area added to parent.children[]?
 
     // verify change history linking
     // 2nd change record: parent (country)
-    expect(nvAreaHistory[1].fullDocument._change?.historyId).toEqual(areaHistory[0]._id) // should point to current change
-    expect(nvAreaHistory[1].fullDocument._change?.prevHistoryId).toEqual(areaHistory[1]._id) // should point to previous Add new area
-
-    // Verify OR history
-    const orAreaHistory = areaHistory[1].changes
-    expect(orAreaHistory).toHaveLength(2)
-
-    const randomHistory = await ChangeLogDataSource.getInstance().getAreaChangeSets(muuid.v4())
-    expect(randomHistory).toHaveLength(0)
-
-    // Verify USA history
-    const usaHistory = await ChangeLogDataSource.getInstance().getAreaChangeSets(usa.metadata.area_id)
-    expect(usaHistory).toHaveLength(2)
-    expect(usaHistory[0].operation).toEqual('addArea')
-    expect(usaHistory[1].operation).toEqual('addArea')
+    expect(
+      nvAreaHistory[1].fullDocument._change?.historyId.equals(
+        initialHistory[0]._id
+      )
+    ) // should point to current change
+    expect(
+      nvAreaHistory[1].fullDocument._change?.prevHistoryId?.equals(
+        initialHistory[1]._id
+      )
+    ) // should point to previous Add new area
+
+    // Verify parent history
+    const countryHistory2 = await changeLog.getAreaChangeSets(
+      area.metadata.area_id
+    )
+    expect(countryHistory2).toHaveLength(2)
+    expect(countryHistory2[0].operation).toEqual('addArea')
+    expect(countryHistory2[1].operation).toEqual('addArea')
 
     // Verify USA history links
-    expect(usaHistory[0].changes[0])
+    expect(countryHistory2[0].changes[0])
   })
 
-  it('should record multiple Areas.setDestination() calls ', async () => {
-    const canada = await areas.addCountry('can')
-    const squamish = await areas.addArea(testUser, 'squamish', canada.metadata.area_id)
-
-    expect(squamish?._id).toBeTruthy()
-
-    if (squamish != null) {
-      const areaUuid = squamish.metadata.area_id
-      await expect(areas.setDestinationFlag(testUser, muuid.v4(), true)).rejects.toThrow() // non-existent area id. Trx won't be recorded
-
-      await areas.setDestinationFlag(testUser, areaUuid, true)
-      await areas.setDestinationFlag(testUser, areaUuid, false)
-
-      await waitForExpect(() => expect(onChange).toHaveBeenCalledTimes(5))
-      const changset = await ChangeLogDataSource.getInstance().getAreaChangeSets(areaUuid)
-
-      expect(changset).toHaveLength(3)
-      expect(changset[0].operation).toEqual('updateDestination')
-      expect(changset[1].operation).toEqual('updateDestination')
-      expect(changset[2].operation).toEqual('addArea')
-
-      expect(changset[0].changes[0].fullDocument.metadata.isDestination).toStrictEqual(false)
-      expect(changset[1].changes[0].fullDocument.metadata.isDestination).toStrictEqual(true)
-      expect(changset[2].changes[0].fullDocument.metadata.isDestination).toStrictEqual(false) // default
-    }
+  it('should record multiple Areas.setDestination() calls ', async ({
+    user,
+    areas,
+    changeLog,
+    country,
+    area
+  }) => {
+    const areaUuid = area.metadata.area_id
+    await expect(
+      areas.setDestinationFlag(user, muuid.v4(), true)
+    ).rejects.toThrow() // non-existent area id. Trx won't be recorded
+
+    await areas.setDestinationFlag(user, areaUuid, true)
+    await areas.setDestinationFlag(user, areaUuid, false)
+
+    await new Promise((resolve) => setTimeout(resolve, 200))
+    const changset = await changeLog.getAreaChangeSets(areaUuid)
+
+    expect(changset).toHaveLength(3)
+    expect(changset[0].operation).toEqual('updateDestination')
+    expect(changset[1].operation).toEqual('updateDestination')
+    expect(changset[2].operation).toEqual('addArea')
+
+    expect(
+      changset[0].changes[0].fullDocument.metadata.isDestination
+    ).toStrictEqual(false)
+    expect(
+      changset[1].changes[0].fullDocument.metadata.isDestination
+    ).toStrictEqual(true)
+    expect(
+      changset[2].changes[0].fullDocument.metadata.isDestination
+    ).toStrictEqual(false) // default
   })
 
-  it('should record an Areas.deleteArea() call', async () => {
-    const greece = await areas.addCountry('grc')
-    const leonidio = await areas.addArea(testUser, 'Leonidio', greece.metadata.area_id)
+  it('should record an Areas.deleteArea() call', async ({
+    user,
+    areas,
+    changeLog,
+    area,
+    waitForChanges
+  }) => {
+    await areas.deleteArea(user, area.metadata.area_id)
+    await waitForChanges({ document: area, count: 1 })
 
-    if (leonidio == null) fail()
-
-    await areas.deleteArea(testUser, leonidio.metadata.area_id)
-
-    await waitForExpect(() => expect(onChange).toHaveBeenCalledTimes(5))
-    const history = await ChangeLogDataSource.getInstance().getAreaChangeSets(leonidio.metadata.area_id)
+    const history = await changeLog.getAreaChangeSets(area.metadata.area_id)
 
     expect(history).toHaveLength(2)
     expect(history[0].operation).toEqual('deleteArea')
     expect(history[1].operation).toEqual('addArea')
 
-    expect(history[0].changes[0].fullDocument._id).toEqual(leonidio._id)
+    expect(history[0].changes[0].fullDocument._id).toEqual(area._id)
   })
 
-  it('should not record a failed Areas.deleteArea() call', async () => {
-    const spain = await areas.addCountry('esp')
-    const margalef = await areas.addArea(testUser, 'margalef', spain.metadata.area_id)
-
-    if (margalef == null) fail()
-
-    const newChild = await areas.addArea(testUser, 'One', margalef.metadata.area_id)
-
-    if (newChild == null) fail()
-
-    let deleted = false
-    try {
-      await areas.deleteArea(testUser, margalef.metadata.area_id)
-      fail('Shouldn\'t allow deletion when the area still has subareas')
-    } catch (e) {
-      deleted = true
-    }
-
-    expect(deleted).toBeTruthy()
-
-    await waitForExpect(() => expect(onChange).toHaveBeenCalledTimes(5))
-    const history = await ChangeLogDataSource.getInstance().getAreaChangeSets(spain.metadata.area_id)
+  it('should not record a failed Areas.deleteArea() call', async ({
+    user,
+    area,
+    areas,
+    addArea,
+    changeLog,
+    waitForChanges
+  }) => {
+    const process = waitForChanges({ document: area, count: 2 })
+    const child = await addArea(undefined, { parent: area })
+    // by giving this child its own child, we can create a vioalation condition if someone were
+    // to try and delete <child>
+    await addArea(undefined, { parent: child })
+
+    await expect(
+      async () => await areas.deleteArea(user, child.metadata.area_id)
+    ).rejects.toThrow()
+    await process
+
+    const history = await changeLog.getAreaChangeSets(area.metadata.area_id)
 
     // should only have 2 entries:
-    // 1. Add country
-    // 2. Add child to country
-    expect(history).toHaveLength(1)
+    // 1. Add child
+    // 2. Add child to that child
+    expect(history).toHaveLength(2)
     expect(history[0].operation).toEqual('addArea')
+    expect(history[1].operation).toEqual('addArea')
   })
 })
diff --git a/src/model/__tests__/AreaUtils.ts b/src/model/__tests__/AreaUtils.ts
index 0f30849b..306c87eb 100644
--- a/src/model/__tests__/AreaUtils.ts
+++ b/src/model/__tests__/AreaUtils.ts
@@ -1,4 +1,4 @@
-describe('Test area utilities', () => {
+describe.todo('Test area utilities', () => {
   test.todo('The name comparison code unit')
   test.todo('The name-uniqueness system with other side-effects stripped out')
 })
diff --git a/src/model/__tests__/BulkDataSource.test.ts b/src/model/__tests__/BulkDataSource.test.ts
index c9e8744f..a323cb30 100644
--- a/src/model/__tests__/BulkDataSource.test.ts
+++ b/src/model/__tests__/BulkDataSource.test.ts
@@ -1,87 +1,59 @@
-import {ChangeStream} from 'mongodb';
-import muuid from 'uuid-mongodb';
-import ChangeLogDataSource from '../ChangeLogDataSource.js';
-import MutableClimbDataSource from '../MutableClimbDataSource.js';
 import {AreaType} from '../../db/AreaTypes.js';
 import {ClimbType} from '../../db/ClimbTypes.js';
-import streamListener from '../../db/edit/streamListener.js';
-import inMemoryDB from "../../utils/inMemoryDB.js";
 import {isFulfilled} from "../../utils/testUtils.js";
-import BulkImportDataSource from "../BulkImportDataSource.js";
 import {BulkImportAreaInputType, BulkImportResultType} from "../../db/BulkImportTypes.js";
+import { dataFixtures } from '../../__tests__/fixtures/data.fixtures.js';
+
+interface LocalContext {
+  assertBulkImport: (...input: BulkImportAreaInputType[]) => Promise<BulkImportResultType>
+}
+
+const it = dataFixtures.extend<LocalContext>({
+  assertBulkImport: async ({ climbs, user, bulkImport }, use) => {
+    const assertBulkImport = async (...input: BulkImportAreaInputType[]): Promise<BulkImportResultType> => {
+      const result = await bulkImport.bulkImport({
+        user: user,
+        input: {areas: input},
+        climbs
+      });
 
-describe('bulk import e2e', () => {
-  let bulkImport: BulkImportDataSource;
-  let climbs: MutableClimbDataSource;
-  let stream: ChangeStream;
-  const testUser = muuid.v4();
-
-  const assertBulkImport = async (...input: BulkImportAreaInputType[]): Promise<BulkImportResultType> => {
-    const result = await bulkImport.bulkImport({
-      user: testUser,
-      input: {areas: input},
-      climbs
-    });
-
-    const addedAreas = await Promise.allSettled(
-      result.addedAreas.map((area) =>
-        bulkImport.findOneAreaByUUID(area.metadata.area_id)
-      )
-    );
-    const updatedAreas = await Promise.allSettled(
-      result.updatedAreas.map((area) =>
-        bulkImport.findOneAreaByUUID(area.metadata.area_id)
-      )
-    );
-    const addedOrUpdatedClimbs = await Promise.allSettled(
-      result.addedOrUpdatedClimbs.map((climb) => climbs.findOneClimbByMUUID(climb._id))
-    );
-
-    return {
-      addedAreas: addedAreas.filter(isFulfilled).map((p) => p.value),
-      updatedAreas: updatedAreas.filter(isFulfilled).map((p) => p.value),
-      addedOrUpdatedClimbs: addedOrUpdatedClimbs.filter(isFulfilled).map((p) => p.value as ClimbType),
+      const addedAreas = await Promise.allSettled(
+        result.addedAreas.map((area) =>
+          bulkImport.findOneAreaByUUID(area.metadata.area_id)
+        )
+      );
+      const updatedAreas = await Promise.allSettled(
+        result.updatedAreas.map((area) =>
+          bulkImport.findOneAreaByUUID(area.metadata.area_id)
+        )
+      );
+      const addedOrUpdatedClimbs = await Promise.allSettled(
+        result.addedOrUpdatedClimbs.map((climb) => climbs.findOneClimbByMUUID(climb._id))
+      );
+
+      return {
+        addedAreas: addedAreas.filter(isFulfilled).map((p) => p.value),
+        updatedAreas: updatedAreas.filter(isFulfilled).map((p) => p.value),
+        addedOrUpdatedClimbs: addedOrUpdatedClimbs.filter(isFulfilled).map((p) => p.value as ClimbType),
+      };
     };
-  };
-
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-    stream = await streamListener();
-  });
-
-  afterAll(async () => {
-    try {
-      await stream.close();
-      await inMemoryDB.close()
-    } catch (e) {
-      console.log('error closing mongoose', e);
-    }
-  });
-
-  beforeEach(async () => {
-    bulkImport = BulkImportDataSource.getInstance();
-    climbs = MutableClimbDataSource.getInstance();
-
-    await bulkImport.addCountry('us');
-  });
-
-  afterEach(async () => {
-    await ChangeLogDataSource.getInstance()._testRemoveAll();
-    await inMemoryDB.clear()
-  });
+    await use(assertBulkImport)
+  }
+})
 
+describe('bulk import e2e', () => {
   describe('adding new areas and climbs', () => {
-    it('should commit a new minimal area to the database', async () => {
+    it('should commit a new minimal area to the database', async ({ assertBulkImport, country }) => {
       await expect(
         assertBulkImport({
           areaName: 'Minimal Area',
-          countryCode: 'us',
+          countryCode: country.shortCode,
         })
       ).resolves.toMatchObject({
         addedAreas: [
           {
             area_name: 'Minimal Area',
-            gradeContext: 'US',
+            gradeContext: country.gradeContext,
             metadata: {
               leaf: false,
               isBoulder: false,
@@ -91,12 +63,12 @@ describe('bulk import e2e', () => {
       });
     });
 
-    it('should rollback when one of the areas fails to import', async () => {
+    it('should rollback when one of the areas fails to import', async ({ assertBulkImport, country }) => {
       await expect(
         assertBulkImport(
           {
             areaName: 'Test Area',
-            countryCode: 'us',
+            countryCode: country.shortCode,
           },
           {
             areaName: 'Test Area 2',
@@ -105,11 +77,11 @@ describe('bulk import e2e', () => {
       ).rejects.toThrowError("Must provide parent Id or country code");
     });
 
-    it('should import nested areas with children', async () => {
+    it('should import nested areas with children', async ({ assertBulkImport, country }) => {
       await expect(
         assertBulkImport({
           areaName: 'Parent Area',
-          countryCode: 'us',
+          countryCode: country.shortCode,
           children: [
             {
               areaName: 'Child Area 2',
@@ -118,17 +90,17 @@ describe('bulk import e2e', () => {
         })
       ).resolves.toMatchObject({
         addedAreas: [
-          {area_name: 'Parent Area', gradeContext: 'US'},
-          {area_name: 'Child Area 2', gradeContext: 'US'},
+          {area_name: 'Parent Area', gradeContext: country.gradeContext},
+          {area_name: 'Child Area 2', gradeContext: country.gradeContext},
         ] as Partial<AreaType>[],
       });
     });
 
-    it('should import nested areas with children and grandchildren', async () => {
+    it('should import nested areas with children and grandchildren', async ({ assertBulkImport, country }) => {
       await expect(
         assertBulkImport({
           areaName: 'Test Area',
-          countryCode: 'us',
+          countryCode: country.shortCode,
           children: [
             {
               areaName: 'Test Area 2',
@@ -144,12 +116,12 @@ describe('bulk import e2e', () => {
         addedAreas: [
           {
             area_name: 'Test Area',
-            pathTokens: ['United States of America', 'Test Area'],
+            pathTokens: [country.area_name, 'Test Area'],
           },
           {
             area_name: 'Test Area 2',
             pathTokens: [
-              'United States of America',
+              country.area_name,
               'Test Area',
               'Test Area 2',
             ],
@@ -157,7 +129,7 @@ describe('bulk import e2e', () => {
           {
             area_name: 'Test Area 3',
             pathTokens: [
-              'United States of America',
+              country.area_name,
               'Test Area',
               'Test Area 2',
               'Test Area 3',
@@ -167,11 +139,11 @@ describe('bulk import e2e', () => {
       });
     });
 
-    it('should import leaf areas with climbs', async () => {
+    it('should import leaf areas with climbs', async ({ assertBulkImport, country }) => {
       await expect(
         assertBulkImport({
           areaName: 'Test Area',
-          countryCode: 'us',
+          countryCode: country.shortCode,
           climbs: [
             {
               name: 'Test Climb',
@@ -184,7 +156,7 @@ describe('bulk import e2e', () => {
         addedAreas: [
           {
             area_name: 'Test Area',
-            gradeContext: 'US',
+            gradeContext: country.gradeContext,
             metadata: {
               leaf: true,
               isBoulder: false,
@@ -210,16 +182,7 @@ describe('bulk import e2e', () => {
   });
 
   describe('updating existing areas', () => {
-    let area: AreaType;
-    beforeEach(async () => {
-      const result = await assertBulkImport({
-        areaName: 'Existing Area',
-        countryCode: 'us',
-      });
-      area = result.addedAreas[0] as AreaType;
-    });
-
-    it('should update an existing area', async () => {
+    it('should update an existing area', async ({ assertBulkImport, area }) => {
       await expect(
         assertBulkImport({
           uuid: area.metadata.area_id,
diff --git a/src/model/__tests__/ChangeLogDS.ts b/src/model/__tests__/ChangeLogDS.ts
index e722867e..5b6055a0 100644
--- a/src/model/__tests__/ChangeLogDS.ts
+++ b/src/model/__tests__/ChangeLogDS.ts
@@ -1,33 +1,11 @@
 import muuid from 'uuid-mongodb'
-import { getAreaModel, getChangeLogModel } from '../../db/index.js'
-import ChangeLogDataSource from '../ChangeLogDataSource.js'
+import { getChangeLogModel } from '../../db/index.js'
 import { OpType } from '../../db/ChangeLogType.js'
 import { OperationType } from '../../db/AreaTypes.js'
-
-import { logger } from '../../logger.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { dbTest as it } from '../../__tests__/fixtures/mongo.fixtures.js'
 
 describe('Area history', () => {
-  let changeLog: ChangeLogDataSource
-
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-
-    try {
-      await getAreaModel().collection.drop()
-      await getChangeLogModel().collection.drop()
-    } catch (e) {
-      logger.info('Expected exception')
-    }
-
-    changeLog = ChangeLogDataSource.getInstance()
-  })
-
-  afterAll(async () => {
-    await inMemoryDB.close()
-  })
-
-  it('should create a change record', async () => {
+  it('should create a change record', async ({ changeLog }) => {
     const userId = muuid.v4()
     const op: OpType = OperationType.addCountry
 
diff --git a/src/model/__tests__/MediaDataSource.ts b/src/model/__tests__/MediaDataSource.ts
index a99dea54..640d0617 100644
--- a/src/model/__tests__/MediaDataSource.ts
+++ b/src/model/__tests__/MediaDataSource.ts
@@ -1,10 +1,5 @@
 import mongoose from 'mongoose'
-import muuid, { MUUID } from 'uuid-mongodb'
-import MutableMediaDataSource from '../MutableMediaDataSource.js'
-import AreaDataSource from '../MutableAreaDataSource.js'
-import ClimbDataSource from '../MutableClimbDataSource.js'
-
-import { createIndexes } from '../../db/index.js'
+import muuid from 'uuid-mongodb'
 import { AreaType } from '../../db/AreaTypes.js'
 import {
   AddTagEntityInput,
@@ -15,119 +10,76 @@ import {
   AreaMediaQueryInput,
   ClimbMediaQueryInput
 } from '../../db/MediaObjectTypes.js'
-import { newSportClimb1 } from './MutableClimbDataSource.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
-
-const TEST_MEDIA: MediaObjectGQLInput = {
-  userUuid: 'a2eb6353-65d1-445f-912c-53c6301404bd',
-  mediaUrl: '/u/a2eb6353-65d1-445f-912c-53c6301404bd/photo1.jpg',
-  width: 800,
-  height: 600,
-  format: 'jpeg',
-  size: 45000
-}
-
-describe('MediaDataSource', () => {
-  let media: MutableMediaDataSource
-  let areas: AreaDataSource
-  let climbs: ClimbDataSource
-
-  let areaForTagging1: AreaType
-  let areaForTagging2: AreaType
-  let areaForTagging3: AreaType
-  let climbIdForTagging: MUUID
+import { gqlTest } from '../../__tests__/fixtures/gql.fixtures.js'
+import { muuidToString } from '../../utils/helpers.js'
 
-  let areaTag1: AddTagEntityInput
-  let areaTag2: AddTagEntityInput
-  let climbTag: AddTagEntityInput
-
-  let testMediaObject: MediaObject
-
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-
-    areas = AreaDataSource.getInstance()
-    climbs = ClimbDataSource.getInstance()
-    media = MutableMediaDataSource.getInstance()
-  })
+interface LocalContext {
+  mediaInput: MediaObjectGQLInput
+  mediaObject: MediaObject
+  otherArea: AreaType
 
-  beforeEach(async () => {
-    try {
-      await areas.areaModel.collection.drop()
-      await climbs.climbModel.collection.drop()
-      await media.mediaObjectModel.collection.drop()
-    } catch (e) {
-      console.log('Cleaning up db before test')
-    }
-
-    await createIndexes()
-
-    await areas.addCountry('USA')
-    areaForTagging1 = await areas.addArea(muuid.v4(), 'Yosemite NP', null, 'USA')
-    areaForTagging2 = await areas.addArea(muuid.v4(), 'Lake Tahoe', null, 'USA')
-    areaForTagging3 = await areas.addArea(muuid.v4(), 'Shelf Road', null, 'USA')
-    if (areaForTagging1 == null || areaForTagging2 == null || areaForTagging3 == null) fail('Fail to pre-seed test areas')
-
-    const rs = await climbs.addOrUpdateClimbs(muuid.v4(), areaForTagging1.metadata.area_id, [newSportClimb1])
-    if (rs == null) fail('Fail to pre-seed test climbs')
-    climbIdForTagging = muuid.from(rs[0])
-
-    const rs2 = await media.addMediaObjects([TEST_MEDIA])
-    testMediaObject = rs2[0]
-    if (testMediaObject == null) {
-      fail('Fail to create test media')
-    }
-
-    areaTag1 = {
-      mediaId: testMediaObject._id,
-      entityType: 1,
-      entityUuid: areaForTagging1.metadata.area_id
-    }
-
-    areaTag2 = {
-      mediaId: testMediaObject._id,
-      entityType: 1,
-      entityUuid: areaForTagging2.metadata.area_id,
-      topoData: { name: 'AA', value: '1234' }
-    }
-
-    climbTag = {
-      mediaId: testMediaObject._id,
-      entityType: 0,
-      entityUuid: climbIdForTagging
-    }
-  })
+  areaTag1: AddTagEntityInput
+  areaTag2: AddTagEntityInput
+  climbTag: AddTagEntityInput
+}
 
-  afterAll(async () => {
-    await inMemoryDB.close()
+const it = gqlTest.extend<LocalContext>({
+  mediaInput: async ({ task, userUuid }, use) => await use({
+    userUuid,
+    mediaUrl: `/u/${userUuid}/${task.id}.jpg`,
+    width: 800,
+    height: 600,
+    format: 'jpeg',
+    size: 45000
+  }),
+  otherArea: async ({ addArea }, use) => await use(await addArea()),
+  mediaObject: async ({ media, mediaInput }, use) => await use((await media.addMediaObjects([mediaInput]))[0]),
+
+  areaTag1: async ({ area, mediaObject }, use) => await use({
+    mediaId: mediaObject._id,
+    entityType: 1,
+    entityUuid: area.metadata.area_id
+  }),
+
+  areaTag2: async ({ otherArea, mediaObject }, use) => await use({
+    mediaId: mediaObject._id,
+    entityType: 1,
+    entityUuid: otherArea.metadata.area_id,
+    topoData: { name: 'AA', value: '1234' }
+  }),
+
+  climbTag: async ({ climb, mediaObject }, use) => await use({
+    mediaId: mediaObject._id,
+    entityType: 0,
+    entityUuid: climb._id
   })
+})
 
-  it('should not tag a nonexistent area', async () => {
+describe('MediaDataSource', () => {
+  it('should not tag a nonexistent area', async ({ media, mediaObject }) => {
     const badAreaTag: AddTagEntityInput = {
-      mediaId: testMediaObject._id,
+      mediaId: mediaObject._id,
       entityType: 1,
       entityUuid: muuid.v4() // some random area
     }
     await expect(media.upsertEntityTag(badAreaTag)).rejects.toThrow(/area .* not found/i)
   })
 
-  it('should not tag a nonexistent *climb*', async () => {
+  it('should not tag a nonexistent *climb*', async ({ media, mediaObject }) => {
     const badClimbTag: AddTagEntityInput = {
-      mediaId: testMediaObject._id,
+      mediaId: mediaObject._id,
       entityType: 0,
       entityUuid: muuid.v4() // some random climb
     }
     await expect(media.upsertEntityTag(badClimbTag)).rejects.toThrow(/climb .* not found/i)
   })
 
-  it('should tag & remove an area tag', async () => {
-    if (areaForTagging1 == null) fail('Pre-seeded test area not found')
-
+  it('should tag & remove an area tag', async ({ media, mediaInput, areaTag1, climbTag, area, climb }) => {
     // verify the number tags before test
-    let mediaObjects = await media.getOneUserMedia(TEST_MEDIA.userUuid, 10)
+    let mediaObjects = await media.getOneUserMedia(mediaInput.userUuid, 10)
     expect(mediaObjects[0].entityTags).toHaveLength(0)
 
-    // add 1st tag
+    // add 1climbNamest tag
     await media.upsertEntityTag(areaTag1)
 
     // add 2nd tag
@@ -136,14 +88,14 @@ describe('MediaDataSource', () => {
     expect(tag).toMatchObject<Partial<EntityTag>>({
       targetId: climbTag.entityUuid,
       type: climbTag.entityType,
-      areaName: areaForTagging1.area_name,
-      ancestors: areaForTagging1.ancestors,
-      climbName: newSportClimb1.name,
-      lnglat: areaForTagging1.metadata.lnglat
+      areaName: area.area_name,
+      ancestors: area.ancestors,
+      climbName: climb.name,
+      lnglat: area.metadata.lnglat
     })
 
     // verify the number tags
-    mediaObjects = await media.getOneUserMedia(TEST_MEDIA.userUuid, 10)
+    mediaObjects = await media.getOneUserMedia(mediaInput.userUuid, 10)
     expect(mediaObjects[0].entityTags).toHaveLength(2)
 
     // remove tag
@@ -151,14 +103,14 @@ describe('MediaDataSource', () => {
     expect(res).toBe(true)
 
     // verify the number tags
-    mediaObjects = await media.getOneUserMedia(TEST_MEDIA.userUuid, 10)
+    mediaObjects = await media.getOneUserMedia(mediaInput.userUuid, 10)
     expect(mediaObjects[0].entityTags).toHaveLength(1)
   })
 
-  it('should handle delete tag errors gracefully', async () => {
+  it('should handle delete tag errors gracefully', async ({ media, mediaObject }) => {
     // with invalid id format
     await expect(media.removeEntityTag({
-      mediaId: testMediaObject._id,
+      mediaId: mediaObject._id,
       // @ts-expect-error
       tagId: 'abc' // bad ObjectId format
     })).rejects.toThrowError(/Cast to ObjectId failed/i)
@@ -170,28 +122,26 @@ describe('MediaDataSource', () => {
     })).rejects.toThrowError(/not found/i)
   })
 
-  it('should not add a duplicate tag', async () => {
+  it('should not add a duplicate tag', async ({ areaTag2, media }) => {
     const updating = { ...areaTag2, topoData: { name: 'ZZ' } }
     const newTag = await media.upsertEntityTag(updating)
     expect(newTag.targetId).toEqual(areaTag2.entityUuid)
     expect(newTag.topoData).toEqual(updating.topoData)
   })
 
-  it('should not add media with the same url', async () => {
+  it('should not add media with the same url', async ({ mediaInput, media }) => {
     const mediaObj = {
-      ...TEST_MEDIA,
+      ...mediaInput,
       mediaUrl: 'photoAAA.jpg'
     }
     await media.addMediaObjects([mediaObj])
 
-    const rs2 = await expect(media.addMediaObjects([mediaObj])).rejects.toThrowError(/duplicate key error collection/i)
-
-    expect(rs2).toBeUndefined()
+    await expect(async () => await media.addMediaObjects([mediaObj])).rejects.toThrowError('duplicate key error collection')
   })
 
-  it('should delete media', async () => {
+  it('should delete media', async ({ mediaInput, media }) => {
     const rs = await media.addMediaObjects([{
-      ...TEST_MEDIA,
+      ...mediaInput,
       mediaUrl: 'u/a0ca9ebb-aa3b-4bb0-8ddd-7c8b2ed228a5/photo100.jpg'
     }])
 
@@ -200,24 +150,24 @@ describe('MediaDataSource', () => {
     const rs2 = await media.deleteMediaObject(rs[0]._id)
     expect(rs2).toBe(true)
 
-    await expect(media.deleteMediaObject(rs[0]._id)).rejects.toThrowError(/not found/i)
+    await expect(async () => await media.deleteMediaObject(rs[0]._id)).rejects.toThrowError('not found')
   })
 
-  it('should not delete media with non-empty tags', async () => {
+  it('should not delete media with non-empty tags', async ({ mediaInput, media, climb }) => {
     const rs = await media.addMediaObjects([{
-      ...TEST_MEDIA,
+      ...mediaInput,
       mediaUrl: 'photo101.jpg',
-      entityTag: { entityType: 0, entityId: climbIdForTagging.toUUID().toString() }
+      entityTag: { entityType: 0, entityId: climb._id.toUUID().toString() }
     }
     ])
 
-    await expect(media.deleteMediaObject(rs[0]._id)).rejects.toThrowError(/Cannot delete media object with non-empty tags./i)
+    await expect(async () => await media.deleteMediaObject(rs[0]._id)).rejects.toThrowError('Cannot delete media object with non-empty tags.')
   })
 
-  it('should return paginated user media results', async () => {
+  it('should return paginated user media results', async ({ mediaInput, media }) => {
     const ITEMS_PER_PAGE = 3
     const MEDIA_TEMPLATE: MediaObjectGQLInput = {
-      ...TEST_MEDIA,
+      ...mediaInput,
       userUuid: 'a0ca9ebb-aa3b-4bb0-8ddd-7c8b2ed228a5'
     }
 
@@ -233,9 +183,7 @@ describe('MediaDataSource', () => {
 
     const expectedMedia = await media.addMediaObjects(newMediaListInput)
 
-    if (expectedMedia == null) {
-      fail('Seeding test media fail')
-    }
+    assert(expectedMedia != null, 'seeding test media fail')
 
     // reverse because getOneUserMediaPagination() returns most recent first
     expectedMedia.reverse()
@@ -270,10 +218,10 @@ describe('MediaDataSource', () => {
     verifyPageData(page3, MEDIA_TEMPLATE.userUuid, 'userUuid', expectedMedia.slice(6, 7), mediaCount, 1, false)
   })
 
-  it('should return paginated area media results', async () => {
+  it('should return paginated area media results', async ({ mediaInput, area, media }) => {
     const ITEMS_PER_PAGE = 3
     const MEDIA_TEMPLATE: MediaObjectGQLInput = {
-      ...TEST_MEDIA,
+      ...mediaInput,
       userUuid: 'a0ca9ebb-aa3b-4bb0-8ddd-7c8b2ed228a6'
     }
 
@@ -289,54 +237,52 @@ describe('MediaDataSource', () => {
         mediaUrl: `/areaPhoto${i}.jpg`,
         entityTag: {
           entityType: 1,
-          entityId: areaForTagging3.metadata.area_id.toUUID().toString()
+          entityId: area.metadata.area_id.toUUID().toString()
         }
       })
     }
 
     const expectedMedia = await media.addMediaObjects(newMediaListInput)
 
-    if (expectedMedia == null) {
-      fail('Seeding test media fail')
-    }
+    assert(expectedMedia !== null)
 
     // reverse because getOneAreaMediaPagination() returns most recent first
     expectedMedia.reverse()
 
     const input: AreaMediaQueryInput = {
-      areaUuid: muuid.from(areaForTagging3.metadata.area_id),
+      areaUuid: muuid.from(area.metadata.area_id),
       first: ITEMS_PER_PAGE
     }
 
     const page1 = await media.getOneAreaMediaPagination(input)
 
-    verifyPageData(page1, areaForTagging3.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(0, 3), mediaCount, ITEMS_PER_PAGE, true)
+    verifyPageData(page1, area.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(0, 3), mediaCount, ITEMS_PER_PAGE, true)
 
     const page1Edges = page1.mediaConnection.edges
     const input2: AreaMediaQueryInput = {
-      areaUuid: muuid.from(areaForTagging3.metadata.area_id),
+      areaUuid: muuid.from(area.metadata.area_id),
       first: ITEMS_PER_PAGE,
       after: page1Edges[page1Edges.length - 1].cursor
     }
     const page2 = await media.getOneAreaMediaPagination(input2)
 
-    verifyPageData(page2, areaForTagging3.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(3, 6), mediaCount, ITEMS_PER_PAGE, true)
+    verifyPageData(page2, area.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(3, 6), mediaCount, ITEMS_PER_PAGE, true)
 
     const page2Edges = page2.mediaConnection.edges
     const input3: AreaMediaQueryInput = {
-      areaUuid: muuid.from(areaForTagging3.metadata.area_id),
+      areaUuid: muuid.from(area.metadata.area_id),
       first: ITEMS_PER_PAGE,
       after: page2Edges[page2Edges.length - 1].cursor
     }
     const page3 = await media.getOneAreaMediaPagination(input3)
 
-    verifyPageData(page3, areaForTagging3.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(6, 7), mediaCount, 1, false)
+    verifyPageData(page3, area.metadata.area_id.toString(), 'areaUuid', expectedMedia.slice(6, 7), mediaCount, 1, false)
   })
 
-  it('should return paginated climb media results', async () => {
+  it('should return paginated climb media results', async ({ mediaInput, climb, media }) => {
     const ITEMS_PER_PAGE = 4
     const MEDIA_TEMPLATE: MediaObjectGQLInput = {
-      ...TEST_MEDIA,
+      ...mediaInput,
       userUuid: 'a0ca9ebb-aa3b-4bb0-8ddd-7c8b2ed228a7'
     }
 
@@ -352,48 +298,46 @@ describe('MediaDataSource', () => {
         mediaUrl: `/climbPhoto${i}.jpg`,
         entityTag: {
           entityType: 0,
-          entityId: climbIdForTagging.toUUID().toString()
+          entityId: climb._id.toUUID().toString()
         }
       })
     }
 
     const expectedMedia = await media.addMediaObjects(newMediaListInput)
 
-    if (expectedMedia == null) {
-      fail('Seeding test media fail')
-    }
+    assert(expectedMedia !== null)
 
     // reverse because getOneClimbMediaPagination() returns most recent first
     expectedMedia.reverse()
 
     const input: ClimbMediaQueryInput = {
-      climbUuid: muuid.from(climbIdForTagging),
+      climbUuid: muuid.from(climb._id),
       first: ITEMS_PER_PAGE
     }
 
     const page1 = await media.getOneClimbMediaPagination(input)
 
-    verifyPageData(page1, climbIdForTagging.toString(), 'climbUuid', expectedMedia.slice(0, 4), mediaCount, ITEMS_PER_PAGE, true)
+    verifyPageData(page1, muuidToString(climb._id), 'climbUuid', expectedMedia.slice(0, 4), mediaCount, ITEMS_PER_PAGE, true)
 
     const page1Edges = page1.mediaConnection.edges
     const input2: ClimbMediaQueryInput = {
-      climbUuid: muuid.from(climbIdForTagging),
+      climbUuid: climb._id,
       first: ITEMS_PER_PAGE,
       after: page1Edges[page1Edges.length - 1].cursor
     }
     const page2 = await media.getOneClimbMediaPagination(input2)
 
-    verifyPageData(page2, climbIdForTagging.toString(), 'climbUuid', expectedMedia.slice(4, 8), mediaCount, ITEMS_PER_PAGE, true)
+    verifyPageData(page2, muuidToString(climb._id), 'climbUuid', expectedMedia.slice(4, 8), mediaCount, ITEMS_PER_PAGE, true)
 
     const page2Edges = page2.mediaConnection.edges
     const input3: ClimbMediaQueryInput = {
-      climbUuid: muuid.from(climbIdForTagging),
+      climbUuid: climb._id,
       first: ITEMS_PER_PAGE,
       after: page2Edges[page2Edges.length - 1].cursor
     }
     const page3 = await media.getOneClimbMediaPagination(input3)
 
-    verifyPageData(page3, climbIdForTagging.toString(), 'climbUuid', expectedMedia.slice(8, 11), mediaCount, 3, false)
+    verifyPageData(page3, muuidToString(climb._id), 'climbUuid', expectedMedia.slice(8, 11), mediaCount, 3, false)
   })
 })
 
diff --git a/src/model/__tests__/MutableAreaDataSource.test.ts b/src/model/__tests__/MutableAreaDataSource.test.ts
index 3d48c035..f4acf1e6 100644
--- a/src/model/__tests__/MutableAreaDataSource.test.ts
+++ b/src/model/__tests__/MutableAreaDataSource.test.ts
@@ -1,77 +1,27 @@
 import { GraphQLError } from "graphql"
-import { getAreaModel, createIndexes } from "../../db"
-import inMemoryDB from "../../utils/inMemoryDB"
-import MutableAreaDataSource from "../MutableAreaDataSource"
-import muid, { MUUID } from 'uuid-mongodb'
+import muid from 'uuid-mongodb'
 import { AreaType, OperationType } from "../../db/AreaTypes"
 import { ChangeRecordMetadataType } from "../../db/ChangeLogType"
-
+import { dataFixtures as test } from "../../__tests__/fixtures/data.fixtures"
 
 describe("Test area mutations", () => {
-    let areas: MutableAreaDataSource
-    let rootCountry: AreaType
-    let areaCounter = 0
-    const testUser = muid.v4()
-
-    async function addArea(name?: string, extra?: Partial<{ leaf: boolean, boulder: boolean, parent: MUUID | AreaType}>) {
-        function isArea(x: any): x is AreaType {
-            return typeof x.metadata?.area_id !== 'undefined'
-        }
-
-        areaCounter += 1
-        if (name === undefined || name === 'test') {
-            name = process.uptime().toString() + '-' + areaCounter.toString()
-        }
-
-        let parent: MUUID | undefined = undefined
-        if (extra?.parent) {
-            if (isArea(extra.parent)) {
-                parent = extra.parent.metadata?.area_id
-            } else {
-                parent = extra.parent
-            }
-        }
-
-        return areas.addArea(
-            testUser,
-            name,
-            parent ?? rootCountry.metadata.area_id,
-            undefined,
-            undefined,
-            extra?.leaf,
-            extra?.boulder
-        )
-    }
-
-    beforeAll(async () => {
-        await inMemoryDB.connect()
-        await getAreaModel().collection.drop()
-        await createIndexes()
-
-        areas = MutableAreaDataSource.getInstance()
-        // We need a root country, and it is beyond the scope of these tests
-        rootCountry = await areas.addCountry("USA")
-      })
-
-      afterAll(inMemoryDB.close)
-
       describe("Add area param cases", () => {
-        test("Add a simple area with no specifications using a parent UUID", () => areas
-            .addArea(testUser, 'Texas2', rootCountry.metadata.area_id)
+        test("Add a simple area with no specifications using a parent UUID", ({ areas, user, country }) => areas
+            .addArea(user, 'Texas2', country.metadata.area_id)
             .then(area => {
                 expect(area?._change).toMatchObject({
-                    user: testUser,
+                    user: user,
                     operation: OperationType.addArea,
                 } satisfies Partial<ChangeRecordMetadataType>)
             }))
 
         test("Add an area with an unknown UUID parent should fail",
-                async () => await expect(() => areas.addArea(testUser, 'Texas', muid.v4())).rejects.toThrow())
+                async ({ areas, user, country }) => await expect(() => areas.addArea(user, 'Texas', muid.v4())).rejects.toThrow())
 
-        test("Add a simple area with no specifications using a country code", () => areas.addArea(testUser, 'Texas part 2', null, 'USA')
-            .then(texas => areas.addArea(testUser, 'Texas Child', texas.metadata.area_id)))
+        test("Add a simple area with no specifications using a country code", ({ areas, user, country }) => areas.addArea(user, 'Texas part 2', null, country.shortCode)
+            .then(texas => areas.addArea(user, 'Texas Child', texas.metadata.area_id)))
 
-        test("Add a simple area, then specify a new child one level deep", () => addArea('California')
+        test("Add a simple area, then specify a new child one level deep", ({ areas, addArea }) => addArea('California')
             .then(async parent => {
                 let child = await addArea('Child', { parent })
                 expect(child).toMatchObject({ area_name: 'Child' })
@@ -79,14 +29,14 @@ describe("Test area mutations", () => {
                 expect(parentCheck?.children ?? []).toContainEqual(child._id)
             }))
 
-        test("Add a leaf area", () => addArea('Somewhere').then(parent => addArea('Child', { leaf: true, parent }))
+        test("Add a leaf area", ({ areas, addArea }) => addArea('Somewhere').then(parent => addArea('Child', { leaf: true, parent }))
             .then(async leaf => {
                 expect(leaf).toMatchObject({ metadata: { leaf: true }})
                 let area = await areas.areaModel.findById(leaf._id)
                 expect(area).toMatchObject({ metadata: { leaf: true }})
             }))
 
-        test("Add a leaf area that is a boulder", () => addArea('Maine')
+        test("Add a leaf area that is a boulder", ({ addArea }) => addArea('Maine')
             .then(parent => addArea('Child', {leaf: true, boulder: true, parent} ))
             .then(area => {
                 expect(area).toMatchObject({
@@ -97,7 +47,7 @@ describe("Test area mutations", () => {
                 } satisfies Partial<Omit<AreaType, 'metadata'> & { metadata: Partial<AreaType['metadata']>}>)
             }))
 
-        test("Add a NON-leaf area that is a boulder", () => addArea('Wisconcin')
+        test("Add a NON-leaf area that is a boulder", ({ addArea }) => addArea('Wisconcin')
             .then(texas => addArea('Child', { leaf: false, boulder: true }))
             .then(area => {
                 expect(area).toMatchObject({
@@ -110,19 +60,19 @@ describe("Test area mutations", () => {
                 } satisfies Partial<Omit<AreaType, 'metadata'> & { metadata: Partial<AreaType['metadata']>}>)
             }))
 
-        test("Adding a child to a leaf area should cause it to become a normal area", () => addArea()
+        test("Adding a child to a leaf area should cause it to become a normal area", ({ addArea }) => addArea()
             .then(parent => Promise.all(new Array(5).map(() => addArea('test', { leaf: true, parent } ))))
             .then(([leaf]) => leaf)
             .then(leaf => addArea('test', { parent: leaf }))
             .then(leaf => expect(leaf).toMatchObject({ metadata: { leaf: false }})))
 
-        test("area names should be unique in their parent context", () => addArea('test').then(async parent => {
+        test("area names should be unique in their parent context", ({ areas, user, country, addArea }) => addArea('test').then(async parent => {
             await addArea('Big ol boulder', { parent })
             await expect(() => addArea('Big ol boulder', { parent })).rejects.toThrow(GraphQLError)
         }))
       })
 
-      test("Delete Area", () => addArea("test").then(area => areas.deleteArea(testUser, area.metadata.area_id)).then(async deleted => {
+      test("Delete Area", ({ areas, user, addArea }) => addArea("test").then(area => areas.deleteArea(user, area.metadata.area_id)).then(async deleted => {
         expect(deleted).toBeDefined()
         // TODO: this test fails based on the data returned, which appears to omit the _deleting field.
         let d = await areas.areaModel.findById(deleted?._id)
@@ -132,58 +82,58 @@ describe("Test area mutations", () => {
         expect(d?._deleting).toBeDefined()
       }))
 
-      test("Delete Area that is already deleted should throw", () => addArea("test")
-        .then(area => areas.deleteArea(testUser, area.metadata.area_id))
+      test("Delete Area that is already deleted should throw", ({ areas, user, addArea }) => addArea("test")
+        .then(area => areas.deleteArea(user, area.metadata.area_id))
         .then(async area => {
             expect(area).not.toBeNull()
-            await expect(() => areas.deleteArea(testUser, area!.metadata.area_id)).rejects.toThrow()
+            await expect(() => areas.deleteArea(user, area!.metadata.area_id)).rejects.toThrow()
         }))
 
 
 
       describe("Area update cases", () => {
-        test("Updating an area should superficially pass", () => addArea('test').then(area => areas.updateArea(testUser, area.metadata.area_id, { areaName: `New Name! ${process.uptime()}`})))
-        test("Updating an area should produce a change entry in the changelog", () => addArea('test')
-            .then(area => areas.updateArea(testUser, area.metadata.area_id, { areaName: process.uptime().toString() }))
+        test("Updating an area should superficially pass", ({ areas, user, addArea }) => addArea('test').then(area => areas.updateArea(user, area.metadata.area_id, { areaName: `New Name! ${process.uptime()}`})))
+        test("Updating an area should produce a change entry in the changelog", ({ areas, user, country, addArea }) => addArea('test')
+            .then(area => areas.updateArea(user, area.metadata.area_id, { areaName: process.uptime().toString() }))
             .then(area => {
                 expect(area?._change).toMatchObject({
-                    user: testUser,
+                    user: user,
                     operation: OperationType.updateArea,
                 } satisfies Partial<ChangeRecordMetadataType>)
             }))
 
-        test("Area name uniqueness in its current parent context", () => addArea('test').then(async parent => {
+        test("Area name uniqueness in its current parent context", ({ areas, user, addArea }) => addArea('test').then(async parent => {
             let [area, newArea, divorcedArea] = await Promise.all([
                 addArea('original', { parent }),
                 addArea('wannabe', { parent }),
-                addArea(undefined, { parent: rootCountry }),
+                addArea(),
             ])
 
             await Promise.all([
                 // Case where an area gets changed to what it already is, which should not throw an error
-                areas.updateArea(testUser, area.metadata.area_id, { areaName: area.area_name }),
+                areas.updateArea(user, area.metadata.area_id, { areaName: area.area_name }),
                 // name-uniqueness should not be global, so this shouldn't throw
-                areas.updateArea(testUser, divorcedArea.metadata.area_id, { areaName: area.area_name }),
+                areas.updateArea(user, divorcedArea.metadata.area_id, { areaName: area.area_name }),
                 // if we update one of the areas to have a name for which another area already exists, we should expect this to throw.
-                expect(() => areas.updateArea(testUser, newArea.metadata.area_id, { areaName: area.area_name })).rejects.toThrow(GraphQLError),
+                expect(() => areas.updateArea(user, newArea.metadata.area_id, { areaName: area.area_name })).rejects.toThrow(GraphQLError),
             ])
         }))
       })
 
-      test("Area name uniqueness should not create a UUID shadow via deletion", () => addArea('test').then(async parent => {
+      test("Area name uniqueness should not create a UUID shadow via deletion", ({ areas, user, country, addArea }) => addArea('test').then(async parent => {
         let name = 'Big ol boulder'
         let big = await addArea(name, { boulder: true, parent })
-        await areas.deleteArea(testUser, big.metadata.area_id)
+        await areas.deleteArea(user, big.metadata.area_id)
         await addArea(name, { boulder: true, parent })
     }))
 
-    test("Area name uniqueness should not create a UUID shadow via edit of name", () => addArea('test').then(async parent => {
+    test("Area name uniqueness should not create a UUID shadow via edit of name", ({ areas, user, country, addArea }) => addArea('test').then(async parent => {
         let nameShadow = 'Big ol boulder 2'
         let big = await addArea(nameShadow, { boulder: true, parent })
 
         // We change the name of the original owner of the nameshadow, and then try to add a
         // name claming the original name in this area structure context
-        await areas.updateArea(testUser, big.metadata.area_id, { areaName: "Still big ol bolder"})
+        await areas.updateArea(user, big.metadata.area_id, { areaName: "Still big ol bolder"})
         await addArea(nameShadow, { boulder: true, parent })
     }))
 })
\ No newline at end of file
diff --git a/src/model/__tests__/MutableClimbDataSource.ts b/src/model/__tests__/MutableClimbDataSource.ts
index f78fbb5a..87d7eb81 100644
--- a/src/model/__tests__/MutableClimbDataSource.ts
+++ b/src/model/__tests__/MutableClimbDataSource.ts
@@ -1,18 +1,9 @@
 import muid from 'uuid-mongodb'
-import { ChangeStream } from 'mongodb'
-
-import MutableClimbDataSource from '../MutableClimbDataSource.js'
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-
-import { createIndexes, getAreaModel, getClimbModel } from '../../db/index.js'
-import { logger } from '../../logger.js'
 import { ClimbChangeInputType, ClimbType } from '../../db/ClimbTypes.js'
 import { sanitizeDisciplines } from '../../GradeUtils.js'
-import streamListener from '../../db/edit/streamListener.js'
-import ChangeLogDataSource from '../ChangeLogDataSource.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { dataFixtures as it } from '../../__tests__/fixtures/data.fixtures'
 
-export const newSportClimb1: ClimbChangeInputType = {
+const newSportClimb1: ClimbChangeInputType = {
   name: 'Cool route 1',
   disciplines: {
     sport: true
@@ -24,11 +15,6 @@ export const newSportClimb1: ClimbChangeInputType = {
 }
 
 describe('Climb CRUD', () => {
-  let climbs: MutableClimbDataSource
-  let areas: MutableAreaDataSource
-  let stream: ChangeStream
-  const testUser = muid.v4()
-
   const newClimbsToAdd: ClimbChangeInputType[] = [
     {
       name: 'Sport 1',
@@ -139,44 +125,16 @@ describe('Climb CRUD', () => {
     ]
   }
 
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-    stream = await streamListener()
-
-    try {
-      await getAreaModel().collection.drop()
-      await getClimbModel().collection.drop()
-    } catch (e) {
-      logger.info('Expected exception')
-    }
-
-    await createIndexes()
-
-    climbs = MutableClimbDataSource.getInstance()
-    areas = MutableAreaDataSource.getInstance()
-    await ChangeLogDataSource.getInstance()._testRemoveAll()
-    await areas.addCountry('fr')
-  })
-
-  afterAll(async () => {
-    try {
-      await stream.close()
-      await inMemoryDB.close()
-    } catch (e) {
-      console.log('closing mongoose', e)
-    }
-  })
-
-  it('can add new climbs', async () => {
+  it('can add new climbs', async ({ areas, climbs, user }) => {
     await areas.addCountry('usa')
 
-    const newDestination = await areas.addArea(testUser, 'California', null, 'usa')
-    if (newDestination == null) fail('Expect new area to be created')
+    const newDestination = await areas.addArea(user, 'California', null, 'usa')
+    expect(newDestination).toBeTruthy()
 
-    const routesArea = await areas.addArea(testUser, 'Sport & Trad', newDestination.metadata.area_id)
+    const routesArea = await areas.addArea(user, 'Sport & Trad', newDestination.metadata.area_id)
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       routesArea.metadata.area_id,
       newClimbsToAdd)
 
@@ -197,25 +155,25 @@ describe('Climb CRUD', () => {
 
     // California contains subareas.  Should fail.
     await expect(
-      climbs.addOrUpdateClimbs(testUser, newDestination.metadata.area_id, [newBoulderProblem1])
+      climbs.addOrUpdateClimbs(user, newDestination.metadata.area_id, [newBoulderProblem1])
     ).rejects.toThrowError(/You can only add climbs to a crag/)
 
     // Route-only area should accept new boulder problems
-    await climbs.addOrUpdateClimbs(testUser, routesArea.metadata.area_id, [newBoulderProblem1])
+    await climbs.addOrUpdateClimbs(user, routesArea.metadata.area_id, [newBoulderProblem1])
   })
 
-  it('can add new boulder problems', async () => {
+  it('can add new boulder problems', async ({ areas, climbs, user }) => {
     await areas.addCountry('esp')
 
-    const newDestination = await areas.addArea(testUser, 'Valencia', null, 'esp')
-    if (newDestination == null) fail('Expect new area to be created')
+    const newDestination = await areas.addArea(user, 'Valencia', null, 'esp')
+    expect(newDestination).toBeTruthy()
 
-    const boulderingArea = await areas.addArea(testUser, 'Bouldering only', newDestination.metadata.area_id)
+    const boulderingArea = await areas.addArea(user, 'Bouldering only', newDestination.metadata.area_id)
 
     expect(boulderingArea.metadata.isBoulder).toBeFalsy()
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       boulderingArea.metadata.area_id,
       [newBoulderProblem1, newBoulderProblem2])
 
@@ -223,16 +181,14 @@ describe('Climb CRUD', () => {
 
     const newClimb = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
 
-    if (newClimb == null) fail('Expecting new boulder problem to be added, but didn\'t find one')
+    assert(newClimb != null)
     expect(newClimb.name).toBe(newBoulderProblem1.name)
   })
 
-  it('can delete new boulder problems', async () => {
-    const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'fr')
-    if (newBoulderingArea == null) fail('Expect new area to be created')
-
+  it('can delete new boulder problems', async ({ areas, climbs, user, addArea }) => {
+    const newBoulderingArea = await addArea('Bouldering area 1')
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       newBoulderingArea.metadata.area_id,
       [newBoulderProblem1, newBoulderProblem2])
 
@@ -240,20 +196,20 @@ describe('Climb CRUD', () => {
 
     // delete a random (non-existing) climb
     const count0 = await climbs.deleteClimbs(
-      testUser,
+      user,
       newBoulderingArea.metadata.area_id,
       [muid.v4()])
     expect(count0).toEqual(0)
 
     // try delete a correct climb and a non-existent one
     const count1 = await climbs.deleteClimbs(
-      testUser,
+      user,
       newBoulderingArea.metadata.area_id,
       [muid.from(newIDs[0]), muid.v4()])
 
     // immediately delete a previously deleted climb.  Should be a no op.
     const count2 = await climbs.deleteClimbs(
-      testUser,
+      user,
       newBoulderingArea.metadata.area_id,
       [muid.from(newIDs[0]), muid.v4()])
 
@@ -270,7 +226,7 @@ describe('Climb CRUD', () => {
 
     // expect one to remain
     rs = await climbs.findOneClimbByMUUID(muid.from(newIDs[1]))
-    if (rs == null) fail('Expect climb 2 to exist')
+    assert(rs != null)
     expect(rs._id.toUUID().toString()).toEqual(newIDs[1])
 
     const areaRs = await areas.findOneAreaByUUID(newBoulderingArea.metadata.area_id)
@@ -278,13 +234,13 @@ describe('Climb CRUD', () => {
     expect((areaRs.climbs[0] as ClimbType)._id.toUUID().toString()).toEqual(newIDs[1])
   })
 
-  it('handles mixed grades and disciplines correctly', async () => {
+  it('handles mixed grades and disciplines correctly', async ({ areas, climbs, user }) => {
     await areas.addCountry('can')
-    const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'can')
-    if (newBoulderingArea == null) fail('Expect new area to be created')
+    const newBoulderingArea = await areas.addArea(user, 'Bouldering area 1', null, 'can')
+    expect(newBoulderingArea).toBeTruthy()
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       newBoulderingArea.metadata.area_id,
       [{ ...newBoulderProblem1, grade: 'V3' }, // good grade
         { ...newBoulderProblem2, grade: '5.9' }]) // invalid grade (YDS grade for a boulder problem)
@@ -298,13 +254,13 @@ describe('Climb CRUD', () => {
     expect(climb2?.grades).toEqual(undefined)
   })
 
-  it('handles Australian grade context correctly', async () => {
+  it('handles Australian grade context correctly', async ({ areas, climbs, user }) => {
     await areas.addCountry('aus')
 
     {
       // A roped climbing area
-      const newClimbingArea = await areas.addArea(testUser, 'Climbing area 1', null, 'aus')
-      if (newClimbingArea == null) fail('Expect new area to be created')
+      const newClimbingArea = await areas.addArea(user, 'Climbing area 1', null, 'aus')
+      expect(newClimbingArea).toBeTruthy()
 
       const newclimbs = [
         { ...newSportClimb1, grade: '17' }, // good sport grade
@@ -315,7 +271,7 @@ describe('Climb CRUD', () => {
       ]
 
       const newIDs = await climbs.addOrUpdateClimbs(
-        testUser,
+        user,
         newClimbingArea.metadata.area_id,
         newclimbs
       )
@@ -350,11 +306,11 @@ describe('Climb CRUD', () => {
 
     {
       // A bouldering area
-      const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'aus')
-      if (newBoulderingArea == null) fail('Expect new area to be created')
+      const newBoulderingArea = await areas.addArea(user, 'Bouldering area 1', null, 'aus')
+      expect(newBoulderingArea).toBeTruthy()
 
       const newIDs = await climbs.addOrUpdateClimbs(
-        testUser,
+        user,
         newBoulderingArea.metadata.area_id,
         [{ ...newBoulderProblem1, grade: 'V3' }, // good grade
           { ...newBoulderProblem2, grade: '23' }, // bad boulder grade
@@ -373,13 +329,13 @@ describe('Climb CRUD', () => {
     }
   })
 
-  it('handles Brazilian grade context correctly', async () => {
+  it('handles Brazilian grade context correctly', async ({ areas, climbs, user }) => {
     await areas.addCountry('bra')
 
     {
       // A roped climbing area
-      const newClimbingArea = await areas.addArea(testUser, 'Climbing area in Brazil', null, 'bra')
-      if (newClimbingArea == null) fail('Expect new area to be created in Brazil')
+      const newClimbingArea = await areas.addArea(user, 'Climbing area in Brazil', null, 'bra')
+      expect(newClimbingArea).toBeTruthy()
 
       const newclimbs = [
         { ...newSportClimb1, grade: 'VIsup' }, // good sport grade
@@ -390,7 +346,7 @@ describe('Climb CRUD', () => {
       ]
 
       const newIDs = await climbs.addOrUpdateClimbs(
-        testUser,
+        user,
         newClimbingArea.metadata.area_id,
         newclimbs
       )
@@ -425,11 +381,11 @@ describe('Climb CRUD', () => {
 
     {
       // A bouldering area
-      const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'bra')
-      if (newBoulderingArea == null) fail('Expect new area to be created')
+      const newBoulderingArea = await areas.addArea(user, 'Bouldering area 1', null, 'bra')
+      expect(newBoulderingArea).toBeTruthy()
 
       const newIDs = await climbs.addOrUpdateClimbs(
-        testUser,
+        user,
         newBoulderingArea.metadata.area_id,
         [{ ...newBoulderProblem1, grade: 'V3' }, // good grade
           { ...newBoulderProblem2, grade: '23' }, // bad boulder grade
@@ -448,15 +404,15 @@ describe('Climb CRUD', () => {
     }
   })
 
-  it('handles UIAA grades correctly', async () => {
+  it('handles UIAA grades correctly', async ({ areas, climbs, user }) => {
     await areas.addCountry('deu') // Assuming Germany since UIAA is dominant grading system
 
     // A roped climbing area
-    const newClimbingArea = await areas.addArea(testUser, 'Climbing area 1', null, 'deu')
-    if (newClimbingArea == null) fail('Expect new area to be created')
+    const newClimbingArea = await areas.addArea(user, 'Climbing area 1', null, 'deu')
+    expect(newClimbingArea).toBeTruthy()
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       newClimbingArea.metadata.area_id,
       [{ ...newSportClimb1, grade: '6+' }, // good UIAA grade
         { ...newSportClimb2, grade: '7-' }, // good UIAA grade
@@ -478,31 +434,28 @@ describe('Climb CRUD', () => {
     expect(climb4?.grades).toEqual(undefined)
   })
 
-  it('can update boulder problems', async () => {
-    const newDestination = await areas.addArea(testUser, 'Bouldering area A100', null, 'fr')
-
-    if (newDestination == null) fail('Expect new area to be created')
-
+  it('can update boulder problems', async ({ climbs, user, area, randomGrade, gradeSystemFor }) => {
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
-      newDestination.metadata.area_id,
+      user,
+      area.metadata.area_id,
       [newBoulderProblem1, newBoulderProblem2])
 
     const actual0 = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
+    assert(actual0 != null)
 
     expect(actual0).toMatchObject({
       name: newBoulderProblem1.name,
       type: sanitizeDisciplines(newBoulderProblem1.disciplines)
     })
 
-    expect(actual0?.createdBy?.toUUID().toString()).toEqual(testUser.toString())
+    expect(actual0?.createdBy?.toUUID().toString()).toEqual(user.toString())
     expect(actual0?.updatedBy).toBeUndefined()
 
     const changes: ClimbChangeInputType[] = [
       {
         id: newIDs[0],
         name: 'new name A100',
-        grade: '6b',
+        grade: randomGrade(actual0),
         disciplines: sanitizeDisciplines({ bouldering: true })
       },
       {
@@ -512,17 +465,15 @@ describe('Climb CRUD', () => {
     ]
 
     const otherUser = muid.v4()
-
-    const updated = await climbs.addOrUpdateClimbs(otherUser, newDestination.metadata.area_id, changes)
+    const updated = await climbs.addOrUpdateClimbs(otherUser, area.metadata.area_id, changes)
 
     expect(updated).toHaveLength(2)
 
-    const actual1 = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
-
-    expect(actual1).toMatchObject({
+    const climbInDatabase = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
+    expect(climbInDatabase).toMatchObject({
       name: changes[0].name,
       grades: {
-        font: changes[0].grade
+        [gradeSystemFor(actual0)]: changes[0].grade
       },
       // Make sure update doesn't touch other fields
       type: sanitizeDisciplines(changes[0].disciplines),
@@ -533,17 +484,15 @@ describe('Climb CRUD', () => {
       }
     })
 
-    expect(actual1?.createdBy?.toUUID().toString()).toEqual(testUser.toUUID().toString())
-    expect(actual1?.updatedBy?.toUUID().toString()).toEqual(otherUser.toUUID().toString())
+    expect(climbInDatabase?.createdBy?.toUUID().toString()).toEqual(user.toUUID().toString())
+    expect(climbInDatabase?.updatedBy?.toUUID().toString()).toEqual(otherUser.toUUID().toString())
   })
 
-  it('can update climb length, boltsCount & fa', async () => {
-    const newDestination = await areas.addArea(testUser, 'Sport area Z100', null, 'fr')
-
-    if (newDestination == null) fail('Expect new area to be created')
+  it('can update climb length, boltsCount & fa', async ({ areas, climbs, user, addArea }) => {
+    const newDestination = await addArea('Sport area Z100')
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       newDestination.metadata.area_id,
       newClimbsToAdd
     )
@@ -555,9 +504,10 @@ describe('Climb CRUD', () => {
       boltsCount: 5
     }
 
-    await climbs.addOrUpdateClimbs(testUser,
+    await climbs.addOrUpdateClimbs(user,
       newDestination.metadata.area_id,
-      [change])
+      [change]
+    )
 
     const actual = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
 
@@ -572,17 +522,17 @@ describe('Climb CRUD', () => {
     })
   })
 
-  it('can add multi-pitch climbs', async () => {
+  it('can add multi-pitch climbs', async ({ areas, climbs, user }) => {
     await areas.addCountry('aut')
 
-    const newDestination = await areas.addArea(testUser, 'Some Location with Multi-Pitch Climbs', null, 'aut')
-    if (newDestination == null) fail('Expect new area to be created')
+    const newDestination = await areas.addArea(user, 'Some Location with Multi-Pitch Climbs', null, 'aut')
+    expect(newDestination).toBeTruthy()
 
-    const routesArea = await areas.addArea(testUser, 'Sport & Trad Multi-Pitches', newDestination.metadata.area_id)
+    const routesArea = await areas.addArea(user, 'Sport & Trad Multi-Pitches', newDestination.metadata.area_id)
 
     // create new climb with individual pitches
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       routesArea.metadata.area_id,
       [newClimbWithPitches]
     )
@@ -602,25 +552,23 @@ describe('Climb CRUD', () => {
       },
       pitches: newClimbWithPitches.pitches
     })
-    // Validate each pitch
-    if (climb?.pitches != null) {
-      climb.pitches.forEach((pitch) => {
-        expect(pitch).toHaveProperty('_id')
-        expect(pitch).toHaveProperty('parentId')
-        expect(pitch).toHaveProperty('pitchNumber')
-      })
-    } else {
-      fail('Pitches are missing either of required attributes id, parentId, pitchNumber')
-    }
+
+    assert(climb?.pitches != null)
+
+    climb.pitches.forEach((pitch) => {
+      expect(pitch).toHaveProperty('_id')
+      expect(pitch).toHaveProperty('parentId')
+      expect(pitch).toHaveProperty('pitchNumber')
+    })
   })
 
-  it('can update multi-pitch problems', async () => {
-    const newDestination = await areas.addArea(testUser, 'Some Multi-Pitch Area to be Updated', null, 'deu')
+  it('can update multi-pitch problems', async ({ areas, climbs, user }) => {
+    const newDestination = await areas.addArea(user, 'Some Multi-Pitch Area to be Updated', null, 'deu')
 
-    if (newDestination == null) fail('Expect new area to be created')
+    expect(newDestination).toBeTruthy()
 
     const newIDs = await climbs.addOrUpdateClimbs(
-      testUser,
+      user,
       newDestination.metadata.area_id,
       [newClimbWithPitches]
     )
@@ -628,11 +576,9 @@ describe('Climb CRUD', () => {
     // Fetch the original climb
     const original = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
 
-    // Check if 'original' is not null before accessing its properties
-    if ((original == null) || (original.pitches == null) || original.pitches.length < 2) {
-      fail('Original climb is null or does not have at least two pitches (as defined in the test case)')
-      return
-    }
+    assert(original !== null)
+    assert(original.pitches !== undefined)
+    expect(original.pitches.length).not.toBeLessThan(2)
 
     // Store original pitch IDs and parent IDs
     const originalPitch1ID = original.pitches[0]._id.toUUID().toString()
@@ -671,7 +617,7 @@ describe('Climb CRUD', () => {
     ]
 
     // update climb
-    await climbs.addOrUpdateClimbs(testUser, newDestination.metadata.area_id, changes)
+    await climbs.addOrUpdateClimbs(user, newDestination.metadata.area_id, changes)
 
     // Fetch the updated climb
     const updatedClimb = await climbs.findOneClimbByMUUID(muid.from(newIDs[0]))
@@ -700,12 +646,11 @@ describe('Climb CRUD', () => {
       }
 
       // Check that the createdBy and updatedBy fields are not undefined before accessing their properties
-      if ((updatedClimb.createdBy != null) && (updatedClimb.updatedBy != null)) {
-        expect(updatedClimb.createdBy.toUUID().toString()).toEqual(testUser.toString())
-        expect(updatedClimb.updatedBy.toUUID().toString()).toEqual(testUser.toString())
-      } else {
-        fail('createdBy or updatedBy is undefined')
-      }
+      assert(updatedClimb.createdBy !== undefined)
+      assert(updatedClimb.updatedBy !== undefined)
+
+      expect(updatedClimb.createdBy.toUUID().toString()).toEqual(user.toString())
+      expect(updatedClimb.updatedBy.toUUID().toString()).toEqual(user.toString())
     }
   })
 })
diff --git a/src/model/__tests__/MutableOrganizationDataSource.ts b/src/model/__tests__/MutableOrganizationDataSource.ts
index 651198b5..0c371886 100644
--- a/src/model/__tests__/MutableOrganizationDataSource.ts
+++ b/src/model/__tests__/MutableOrganizationDataSource.ts
@@ -1,68 +1,52 @@
 import muuid from 'uuid-mongodb'
 
-import MutableOrganizationDataSource from '../MutableOrganizationDataSource.js'
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-import { createIndexes, getAreaModel, getOrganizationModel } from '../../db/index.js'
-import { OrganizationEditableFieldsType, OrgType } from '../../db/OrganizationTypes.js'
+import { OrganizationEditableFieldsType, OrganizationType, OrgType } from '../../db/OrganizationTypes.js'
 import { AreaType } from '../../db/AreaTypes.js'
 import { muuidToString } from '../../utils/helpers.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { dataFixtures } from '../../__tests__/fixtures/data.fixtures.js'
 
-describe('Organization', () => {
-  let organizations: MutableOrganizationDataSource
-  let areas: MutableAreaDataSource
-  let usa: AreaType
-  let ca: AreaType
-  let wa: AreaType
-  let fullOrg: OrganizationEditableFieldsType
-  let emptyOrg: OrganizationEditableFieldsType
-  const testUser = muuid.v4()
+interface LocalContext {
+  excludedArea: AreaType
+  orgData: OrganizationEditableFieldsType
+  organization: OrganizationType
+  emptyOrg: OrganizationEditableFieldsType
+}
 
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-    try { // Use the same fixed areas for testing so no need to drop and re-create on each test.
-      await getAreaModel().collection.drop()
-    } catch (e) {
-      console.log('Cleaning up area model before test', e)
-    }
-    organizations = MutableOrganizationDataSource.getInstance()
-    areas = MutableAreaDataSource.getInstance()
-    usa = await areas.addCountry('usa')
-    ca = await areas.addArea(testUser, 'CA', usa.metadata.area_id)
-    wa = await areas.addArea(testUser, 'WA', usa.metadata.area_id)
-    fullOrg = {
-      associatedAreaIds: [usa.metadata.area_id],
-      excludedAreaIds: [ca.metadata.area_id, wa.metadata.area_id],
-      displayName: 'Friends of Openbeta',
-      website: 'https://www.friendsofopenbeta.com',
-      email: 'admin@friendsofopenbeta.com',
-      donationLink: 'https://www.friendsofopenbeta.com/donate',
-      instagramLink: 'https://www.instagram.com/friendsofopenbeta',
-      facebookLink: 'https://www.facebook.com/friendsofopenbeta',
-      hardwareReportLink: 'https://www.friendsofopenbeta.com/reporthardware',
-      description: 'We are friends of openbeta.\nWe are a 503(B) corporation.'
-    }
-    emptyOrg = {
-      displayName: 'Foes of Openbeta'
-    }
-  })
+const it = dataFixtures.extend<LocalContext>({
+  excludedArea: async ({ addArea }, use) => { await use(await addArea()) },
 
-  beforeEach(async () => {
-    try {
-      await getOrganizationModel().collection.drop()
-    } catch (e) {
-      console.log('Cleaning up organization model before test', e)
-    }
-    await createIndexes()
-  })
+  orgData: async ({ country, excludedArea, area, task }, use) => {
+    await use({
+      associatedAreaIds: [country.metadata.area_id],
+      excludedAreaIds: [excludedArea.metadata.area_id, area.metadata.area_id],
+      displayName: task.name,
+      website: `https://www.${task.id}.com`,
+      email: `admin@${task.id}.com`,
+      donationLink: `https://www.${task.id}.com/donate`,
+      instagramLink: `https://www.instagram.com/${task.id}`,
+      facebookLink: `https://www.facebook.com/${task.id}`,
+      hardwareReportLink: `https://www.${task.id}.com/reporthardware`,
+      description: `We are ${task.id}.\nWe are a 503(B) corporation.`
+    })
+  },
 
-  afterAll(async () => {
-    await inMemoryDB.close()
-  })
+  organization: async ({ organizations, orgData, user }, use) => {
+    const org = await organizations.addOrganization(user, OrgType.localClimbingOrganization, orgData)
+    await use(org)
+    await organizations.deleteFromCacheById(org._id)
+  },
 
-  it('should successfully create a document when passed valid input', async () => {
-    const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, fullOrg)
-    const document = { ...fullOrg }
+  emptyOrg: async ({ task }, use) => {
+    await use({
+      displayName: `Foes of ${task.id}`
+    })
+  }
+})
+
+describe('Organization', () => {
+  it('should successfully create a document when passed valid input', async ({ organizations, orgData, user, country }) => {
+    const newOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, orgData)
+    const document = { ...orgData }
     expect(newOrg.displayName).toBe(document.displayName)
     expect(newOrg.content?.website).toBe(document.website)
     expect(newOrg.content?.email).toBe(document.email)
@@ -71,7 +55,7 @@ describe('Organization', () => {
     expect(newOrg.content?.facebookLink).toBe(document.facebookLink)
     expect(newOrg.content?.hardwareReportLink).toBe(document.hardwareReportLink)
     expect(newOrg.content?.description).toBe(document.description)
-    expect(newOrg.associatedAreaIds.map(muuidToString)).toEqual([muuidToString(usa.metadata.area_id)])
+    expect(newOrg.associatedAreaIds.map(muuidToString)).toEqual([muuidToString(country.metadata.area_id)])
     expect(newOrg._change?.operation).toBe('addOrganization')
     expect(newOrg._change?.seq).toBe(0)
 
@@ -79,42 +63,55 @@ describe('Organization', () => {
     expect(orgIdSearchRes._id).toEqual(newOrg._id)
   })
 
-  it('should retrieve documents based on displayName', async () => {
-    const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, fullOrg)
-    // Match should be case-insensitive.
-    const displayNameSearchCursor = await organizations.findOrganizationsByFilter({
-      displayName: {
-        match: 'openbeta',
-        exactMatch: false
-      }
+  describe('should retrieve documents based on displayName', () => {
+    it('Should be case insensitive', async ({ organization, organizations }) => {
+      // Match should be case-insensitive.
+      const displayNameSearchCursor = await organizations.findOrganizationsByFilter({
+        displayName: {
+          match: organization.displayName.toLocaleUpperCase(),
+          exactMatch: false
+        }
+      })
+      const displayNameSearchRes = await displayNameSearchCursor.toArray()
+      expect(displayNameSearchRes).toHaveLength(1)
+      expect(displayNameSearchRes[0]._id).toEqual(organization._id)
+    })
+
+    it('Should match against a partial string', async ({ organization, organizations }) => {
+      // Match should be case-insensitive.
+      const displayNameSearchCursor = await organizations.findOrganizationsByFilter({
+        displayName: {
+          match: organization.displayName.toLocaleUpperCase().slice(10, 20),
+          exactMatch: false
+        }
+      })
+      const displayNameSearchRes = await displayNameSearchCursor.toArray()
+      expect(displayNameSearchRes).toHaveLength(1)
+      expect(displayNameSearchRes[0]._id).toEqual(organization._id)
     })
-    const displayNameSearchRes = await displayNameSearchCursor.toArray()
-    expect(displayNameSearchRes).toHaveLength(1)
-    expect(displayNameSearchRes[0]._id).toEqual(newOrg._id)
   })
 
-  it('should retrieve documents based on associatedAreaIds', async () => {
-    const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, fullOrg)
+  it('should retrieve documents based on associatedAreaIds', async ({ organizations, orgData, user, excludedArea, area }) => {
+    const newOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, orgData)
     const document = {
-      associatedAreaIds: [ca.metadata.area_id, wa.metadata.area_id]
+      associatedAreaIds: [excludedArea.metadata.area_id, area.metadata.area_id]
     }
-    await organizations.updateOrganization(testUser, newOrg.orgId, document)
-    const areaIdSearchCursor = await organizations.findOrganizationsByFilter({ associatedAreaIds: { includes: [ca.metadata.area_id] } })
+    await organizations.updateOrganization(user, newOrg.orgId, document)
+    const areaIdSearchCursor = await organizations.findOrganizationsByFilter({ associatedAreaIds: { includes: [excludedArea.metadata.area_id] } })
     const areaIdSearchRes = await areaIdSearchCursor.toArray()
     expect(areaIdSearchRes).toHaveLength(1)
     expect(areaIdSearchRes[0]._id).toEqual(newOrg._id)
   })
 
   describe('update', () => {
-    it('should succeed on valid input', async () => {
-      const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, emptyOrg)
-      const document = { ...fullOrg }
-      const updatedOrg = await organizations.updateOrganization(testUser, newOrg.orgId, document)
+    it('should succeed on valid input', async ({ organizations, emptyOrg, user, orgData }) => {
+      const newOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, emptyOrg)
+      const document = { ...orgData }
+      const updatedOrg = await organizations.updateOrganization(user, newOrg.orgId, document)
 
       expect(updatedOrg).toBeDefined()
-      if (updatedOrg == null) {
-        fail('should not reach here.')
-      }
+      assert(updatedOrg != null)
+
       expect(updatedOrg.associatedAreaIds.map(muuidToString).sort())
         .toStrictEqual(document?.associatedAreaIds?.map(muuidToString).sort())
       expect(updatedOrg.excludedAreaIds.map(muuidToString).sort())
@@ -132,13 +129,13 @@ describe('Organization', () => {
       expect(updatedOrg.updatedAt?.getTime()).toBeGreaterThan(updatedOrg.createdAt.getTime())
     })
 
-    it('should throw when an invalid area is supplied', async () => {
-      const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, emptyOrg)
+    it('should throw when an invalid area is supplied', async ({ orgData, emptyOrg, user, organizations }) => {
+      const newOrg = await organizations.addOrganization(user, OrgType.localClimbingOrganization, emptyOrg)
       const document = {
-        ...fullOrg,
+        ...orgData,
         associatedAreaIds: [muuid.v4()]
       }
-      await expect(organizations.updateOrganization(testUser, newOrg.orgId, document))
+      await expect(organizations.updateOrganization(user, newOrg.orgId, document))
         .rejects
         .toThrow(/Organization update error. Reason: Associated areas not found: /)
     })
diff --git a/src/model/__tests__/UserDataSource.ts b/src/model/__tests__/UserDataSource.ts
index 1a63bef9..b5e5e722 100644
--- a/src/model/__tests__/UserDataSource.ts
+++ b/src/model/__tests__/UserDataSource.ts
@@ -1,36 +1,14 @@
-import mongoose from 'mongoose'
 import muuid from 'uuid-mongodb'
-import { jest } from '@jest/globals'
-
-import { getUserModel } from '../../db/index.js'
 import UserDataSource from '../UserDataSource.js'
 import { UpdateProfileGQLInput } from '../../db/UserTypes.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { dataFixtures as it } from '../../__tests__/fixtures/data.fixtures.js'
 
 describe('UserDataSource', () => {
-  let users: UserDataSource
-
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-    const userModel = getUserModel()
-    try {
-      await userModel.collection.drop()
-    } catch (e) {
-      console.log('Cleaning up db before test')
-    }
-    await userModel.ensureIndexes()
-    users = new UserDataSource({ modelOrCollection: mongoose.connection.db.collection('users') })
-  })
-
-  afterAll(async () => {
-    await inMemoryDB.close()
-  })
-
   afterEach(() => {
-    jest.clearAllMocks()
+    vi.clearAllMocks()
   })
 
-  it('should create a new user with just username', async () => {
+  it('should create a new user with just username', async ({ users }) => {
     const userUuid = muuid.v4()
     const updater = muuid.v4()
     const input: UpdateProfileGQLInput = {
@@ -53,7 +31,7 @@ describe('UserDataSource', () => {
     expect(u?.updatedAt.getTime()).toBeLessThan(Date.now())
   })
 
-  it('should create a new user from username and other updatable fields', async () => {
+  it('should create a new user from username and other updatable fields', async ({ users }) => {
     const updater = muuid.v4()
     const userUuid = muuid.v4()
     const username = 'new-test-profile'
@@ -100,7 +78,7 @@ describe('UserDataSource', () => {
     })
   })
 
-  it('should require an email when creating new profile', async () => {
+  it('should require an email when creating new profile', async ({ users }) => {
     const updater = muuid.v4()
     const userUuid = muuid.v4()
     const input: UpdateProfileGQLInput = {
@@ -113,7 +91,7 @@ describe('UserDataSource', () => {
     ).rejects.toThrowError(/Email is required/i)
   })
 
-  it('should enforce a waiting period for username update', async () => {
+  it('should enforce a waiting period for username update', async ({ users }) => {
     const updater = muuid.v4()
     const userUuid = muuid.v4()
     const input: UpdateProfileGQLInput = {
@@ -132,7 +110,7 @@ describe('UserDataSource', () => {
     ).rejects.toThrowError(/frequent update/i)
   })
 
-  it('should allow username update after the waiting period', async () => {
+  it('should allow username update after the waiting period', async ({ users }) => {
     const updater = muuid.v4()
     const userUuid = muuid.v4()
     const input: UpdateProfileGQLInput = {
@@ -143,7 +121,7 @@ describe('UserDataSource', () => {
 
     await users.createOrUpdateUserProfile(updater, input)
 
-    jest
+    vi
       .spyOn(UserDataSource, 'calculateLastUpdatedInDays')
       .mockImplementation(() => 14)
 
@@ -159,7 +137,7 @@ describe('UserDataSource', () => {
     expect(updatedUser?.username).toEqual(newInput.username)
   })
 
-  it('should reject invalid website url', async () => {
+  it('should reject invalid website url', async ({ users }) => {
     const updater = muuid.v4()
     const userUuid = muuid.v4()
     const input: UpdateProfileGQLInput = {
diff --git a/src/model/__tests__/tickValidation.ts b/src/model/__tests__/tickValidation.ts
index 98599479..9e3c2904 100644
--- a/src/model/__tests__/tickValidation.ts
+++ b/src/model/__tests__/tickValidation.ts
@@ -1,219 +1,146 @@
-import { produce } from 'immer'
-import TickDataSource from '../TickDataSource.js'
-import { getTickModel, getUserModel } from '../../db/index.js'
-import { TickInput } from '../../db/TickTypes.js'
-import muuid from 'uuid-mongodb'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { allowableStyleMap, choose, dataFixtures } from '../../__tests__/fixtures/data.fixtures.js'
+import { muuidToString } from '../../utils/helpers.js'
 import { ClimbChangeInputType } from '../../db/ClimbTypes.js'
-import MutableClimbDataSource from '../MutableClimbDataSource.js'
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-
-const userId = muuid.v4()
-const newClimbsToAdd: ClimbChangeInputType[] = [
-  {
-    name: 'Sport 1',
-    disciplines: {
-      sport: true
+import {
+  TickInput,
+  TickStyleValues
+} from '../../db/TickTypes.js'
+
+interface LocalContext {
+  tick: (
+    props?: Partial<TickInput> & {
+      climb?: Partial<ClimbChangeInputType>
     },
-    description: 'The best climb',
-    location: '5m left of the big tree',
-    protection: '5 quickdraws'
-  },
-  {
-    name: 'Deep water 1',
-    disciplines: {
-      deepwatersolo: true
-    }
-  },
-  {
-    name: 'Boulder 1',
-    disciplines: {
-      bouldering: true
-    }
-  },
-  {
-    name: 'Top Rope 1',
-    disciplines: {
-      tr: true
-    }
-  },
-  {
-    name: 'Aid 1',
-    disciplines: {
-      aid: true
-    }
-  }
-]
-
-const toTestSport: TickInput = {
-  name: 'Small Dog',
-  notes: 'Sandbagged',
-  climbId: 'tbd', // need to create a climb for tick validation
-  userId: userId.toUUID().toString(),
-  style: 'Lead',
-  attemptType: 'Onsight',
-  dateClimbed: new Date('2012-12-12'),
-  grade: '5.7',
-  source: 'MP'
+  ) => Promise<TickInput>
 }
 
-const toTestDWS: TickInput = {
-  name: 'Sloppy Peaches',
-  notes: 'v sloppy',
-  climbId: 'tbd',
-  userId: userId.toUUID().toString(),
-  attemptType: 'Flash',
-  dateClimbed: new Date('2012-10-15'),
-  grade: '5.10',
-  source: 'MP'
-}
-
-const toTestBoulder: TickInput = {
-  name: 'Boulder or DWS',
-  notes: 'wet!',
-  climbId: 'tbd',
-  userId: userId.toUUID().toString(),
-  style: 'Boulder',
-  attemptType: 'Flash',
-  dateClimbed: new Date('2012-10-15'),
-  grade: 'v4',
-  source: 'OB'
-}
-
-const toTestTR: TickInput = {
-  name: 'Top Rope Climb',
-  notes: 'Nice climb',
-  climbId: 'tbd',
-  userId: userId.toUUID().toString(),
-  style: 'TR',
-  attemptType: 'Send',
-  dateClimbed: new Date('2012-10-15'),
-  grade: '5.10',
-  source: 'OB'
-}
-
-const toTestAid: TickInput = {
-  name: 'Aid Climb',
-  notes: 'Challenging',
-  climbId: 'tbd',
-  userId: userId.toUUID().toString(),
-  style: 'Aid',
-  attemptType: 'Send',
-  dateClimbed: new Date('2012-10-15'),
-  grade: 'A2',
-  source: 'OB'
-}
-
-let tickUpdate: TickInput = produce(toTestSport, draft => {
-  draft.notes = 'Not sandbagged'
-  draft.attemptType = 'Flash'
-  draft.source = 'OB'
+const it = dataFixtures.extend<LocalContext>({
+  tick: async ({ userUuid, addClimb }, use) =>
+    await use(async (props) => {
+      const { climb, ...tick } = props ?? {}
+
+      const reifiedClimb = await addClimb(climb)
+      const style = tick.style ?? choose(TickStyleValues)
+      const attemptType = tick.attemptType ?? choose(allowableStyleMap[style])
+      return {
+        name: reifiedClimb.name,
+        notes: 'Sandbagged',
+        climbId: muuidToString(reifiedClimb._id),
+        userId: userUuid,
+        style,
+        attemptType,
+        dateClimbed: new Date('2012-12-12'),
+        grade: '5.7',
+        source: 'MP',
+        ...tick
+      }
+    })
 })
 
 describe('Tick Validation', () => {
-  let ticks: TickDataSource
-  let climbs: MutableClimbDataSource
-  let areas: MutableAreaDataSource
-  const tickModel = getTickModel()
-
-  beforeAll(async () => {
-    console.log('#BeforeAll Tick Validation')
-    await inMemoryDB.connect()
-
-    try {
-      await getTickModel().collection.drop()
-      await getUserModel().collection.drop()
-    } catch (e) {
-      console.log('Cleaning db')
-    }
-
-    ticks = TickDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
-    areas = MutableAreaDataSource.getInstance()
-    // Add climbs because add/update tick requires type validation
-    await areas.addCountry('usa')
-    const newDestination = await areas.addArea(userId, 'California', null, 'usa')
-    if (newDestination == null) fail('Expect new area to be created')
-
-    const routesArea = await areas.addArea(userId, 'Sport & Trad', newDestination.metadata.area_id)
-
-    const newIDs = await climbs.addOrUpdateClimbs(userId, routesArea.metadata.area_id, newClimbsToAdd)
-
-    // Update tick inputs with generated climb IDs
-    toTestSport.climbId = newIDs[0]
-    toTestDWS.climbId = newIDs[1]
-    toTestBoulder.climbId = newIDs[2]
-    toTestTR.climbId = newIDs[3]
-    toTestAid.climbId = newIDs[4]
-    tickUpdate = { ...tickUpdate, climbId: newIDs[0] } // Ensure tickUpdate has the correct climbId
-  })
-
-  afterAll(async () => {
-    await inMemoryDB.close()
+  it('should validate tick for sport climb', async ({ ticks, tick }) => {
+    const tickData = await tick({ style: 'Lead' })
+    await expect(ticks.addTick(tickData)).resolves.not.toThrow()
   })
 
-  afterEach(async () => {
-    await getTickModel().collection.drop()
-    await tickModel.ensureIndexes()
+  it('should validate tick for deep water solo climb', async ({
+    ticks,
+    tick
+  }) => {
+    const tickData = await tick({
+      climb: { disciplines: { deepwatersolo: true } },
+      attemptType: 'Send',
+      style: undefined
+    })
+
+    await expect(ticks.addTick(tickData)).resolves.not.toThrow()
   })
 
-  it('should validate tick for sport climb', async () => {
-    await expect(ticks.addTick(toTestSport)).resolves.not.toThrow()
-  })
-
-  it('should validate tick for deep water solo climb', async () => {
-    const dwsTick: TickInput = {
-      ...toTestDWS,
-      attemptType: 'Send'
-    }
-    await expect(ticks.addTick(dwsTick)).resolves.not.toThrow()
-  })
-
-  it('should throw error for invalid style for deep water solo climb', async () => {
-    const invalidDwsTick: TickInput = {
-      ...toTestDWS,
+  it('should throw error for invalid style for deep water solo climb', async ({
+    ticks,
+    tick
+  }) => {
+    const invalidDwsTick: TickInput = await tick({
       style: 'Lead',
-      attemptType: 'Send'
-    }
-    await expect(ticks.addTick(invalidDwsTick)).rejects.toThrow('Invalid style Lead for climb type')
+      attemptType: 'Send',
+      climb: { disciplines: { deepwatersolo: true } }
+    })
+
+    await expect(ticks.addTick(invalidDwsTick)).rejects.toThrow(
+      'Invalid style Lead for climb type'
+    )
   })
 
-  it('should validate tick for top rope climb', async () => {
-    await expect(ticks.addTick(toTestTR)).resolves.not.toThrow()
+  it('should validate tick for top rope climb', async ({ ticks, tick }) => {
+    const tickData = await tick({
+      style: 'TR',
+      climb: { disciplines: { tr: true } }
+    })
+    await expect(ticks.addTick(tickData)).resolves.not.toThrow()
   })
 
-  it('should throw error for invalid attempt type for top rope climb', async () => {
-    const invalidTrTick: TickInput = {
-      ...toTestTR,
-      attemptType: 'Pinkpoint'
-    }
-    await expect(ticks.addTick(invalidTrTick)).rejects.toThrow('Invalid attempt type Pinkpoint for TR/Follow/Aid style')
+  it('should throw error for invalid attempt type for top rope climb', async ({
+    ticks,
+    tick
+  }) => {
+    const invalidTrTick = await tick({
+      attemptType: 'Pinkpoint',
+      style: 'TR',
+      climb: {
+        disciplines: { tr: true }
+      }
+    })
+
+    await expect(ticks.addTick(invalidTrTick)).rejects.toThrow(
+      'Invalid attempt type Pinkpoint for TR/Follow/Aid style'
+    )
   })
 
-  it('should validate tick for aid climb', async () => {
-    await expect(ticks.addTick(toTestAid)).resolves.not.toThrow()
+  it('should validate tick for aid climb', async ({ ticks, tick }) => {
+    const tickData = await tick({
+      style: 'Aid',
+      climb: { disciplines: { aid: true } }
+    })
+    await expect(ticks.addTick(tickData)).resolves.not.toThrow()
   })
 
-  it('should throw error for invalid attempt type for aid climb', async () => {
-    const invalidAidTick: TickInput = {
-      ...toTestAid,
-      attemptType: 'Flash'
-    }
-    await expect(ticks.addTick(invalidAidTick)).rejects.toThrow('Invalid attempt type Flash for TR/Follow/Aid style')
+  it('should throw error for invalid attempt type for aid climb', async ({
+    ticks,
+    tick
+  }) => {
+    const invalidAidTick = await tick({
+      style: 'Aid',
+      attemptType: 'Flash',
+      climb: { disciplines: { aid: true } }
+    })
+
+    await expect(ticks.addTick(invalidAidTick)).rejects.toThrow(
+      'Invalid attempt type Flash for TR/Follow/Aid style'
+    )
   })
 
-  it('should throw error for invalid style for aid climb', async () => {
-    const invalidAidTick: TickInput = {
-      ...toTestAid,
+  it('should throw error for invalid style for aid climb', async ({
+    ticks,
+    tick
+  }) => {
+    const invalidAidTick: TickInput = await tick({
       style: 'Lead',
-      attemptType: 'Send'
-    }
-    await expect(ticks.addTick(invalidAidTick)).rejects.toThrow('Invalid style Lead for climb type')
+      attemptType: 'Send',
+      grade: 'A2',
+      climb: { disciplines: { aid: true } }
+    })
+
+    await expect(ticks.addTick(invalidAidTick)).rejects.toThrow(
+      'Invalid style Lead for climb type'
+    )
   })
 
-  it('should validate tick with no attempt type', async () => {
+  it('should validate tick with no attempt type', async ({ ticks, tick }) => {
     const noAttemptTypeTick: TickInput = {
-      ...toTestSport,
+      ...(await tick({
+        style: 'Lead',
+        climb: { disciplines: { sport: true } }
+      })),
       attemptType: undefined
     }
     await expect(ticks.addTick(noAttemptTypeTick)).resolves.not.toThrow()
diff --git a/src/model/__tests__/ticks.ts b/src/model/__tests__/ticks.ts
index 96baf716..04ff236c 100644
--- a/src/model/__tests__/ticks.ts
+++ b/src/model/__tests__/ticks.ts
@@ -1,223 +1,135 @@
 import { produce } from 'immer'
-import TickDataSource from '../TickDataSource.js'
-import { getTickModel, getUserModel } from '../../db/index.js'
-import { TickInput } from '../../db/TickTypes.js'
+import { TickInput, TickType } from '../../db/TickTypes.js'
+import { allowableStyleMap, dataFixtures } from '../../__tests__/fixtures/data.fixtures.js'
+import { muuidToString } from '../../utils/helpers.js'
 import muuid from 'uuid-mongodb'
-import UserDataSource from '../UserDataSource.js'
-import { UpdateProfileGQLInput } from '../../db/UserTypes.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
-import { ClimbChangeInputType } from '../../db/ClimbTypes.js'
-import MutableClimbDataSource from '../MutableClimbDataSource.js'
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-
-const userId = muuid.v4()
-const newClimbsToAdd: ClimbChangeInputType[] = [
-  {
-    name: 'Sport 1',
-    // Intentionally disable TS check to make sure input is sanitized
-    disciplines: {
-      sport: true
-    },
-    description: 'The best climb',
-    location: '5m left of the big tree',
-    protection: '5 quickdraws'
-  },
-  {
-    name: 'Deep water 1',
-    disciplines: {
-      deepwatersolo: true
-    }
-  }
-]
-
-const toTest: TickInput = {
-  name: 'Small Dog',
-  notes: 'Sandbagged',
-  climbId: 'tbd', // need to create a climb for tick validation
-  userId: userId.toUUID().toString(),
-  style: 'Lead',
-  attemptType: 'Onsight',
-  dateClimbed: new Date('2012-12-12'),
-  grade: '5.7',
-  source: 'MP'
+
+interface LocalContext {
+  tickImportData: TickInput[]
+  tickData: TickInput
+  tickUpdateData: TickInput
+  tick: TickType
 }
 
-const toTest2: TickInput = {
-  name: 'Sloppy Peaches',
-  notes: 'v sloppy',
-  climbId: 'tbd',
-  userId: userId.toUUID().toString(),
-  attemptType: 'Flash',
-  dateClimbed: new Date('2012-10-15'),
-  grade: '5.10',
-  source: 'MP'
+function choose<T> (arr: T[]): T {
+  return arr[Math.floor(Math.random() * arr.length)]
 }
 
-let tickUpdate: TickInput = produce(toTest, draft => {
-  draft.notes = 'Not sandbagged'
-  draft.attemptType = 'Flash'
-  draft.source = 'OB'
+const it = dataFixtures.extend<LocalContext>({
+  tickImportData: async ({ task, userUuid, climb }, use) => await use(
+    Array.from({ length: 20 }).map((_, idx) => (
+      {
+        name: `${task.id}-${idx}`,
+        notes: 'Sandbagged',
+        climbId: muuidToString(climb._id),
+        userId: userUuid,
+        style: 'Lead',
+        attemptType: choose(allowableStyleMap.Lead),
+        dateClimbed: new Date(),
+        grade: '5.7',
+        source: 'MP'
+      }
+    ))),
+  tickData: async ({ userUuid, climb }, use) => await use({
+    name: 'Small Dog',
+    notes: 'Sandbagged',
+    climbId: muuidToString(climb._id),
+    userId: userUuid,
+    style: 'Lead',
+    attemptType: choose(allowableStyleMap.Lead),
+    dateClimbed: new Date('2012-12-12'),
+    grade: '5.7',
+    source: 'MP'
+  }),
+
+  tickUpdateData: async ({ tickData }, use) => await use(produce(tickData, draft => {
+    draft.notes = 'Not sandbagged'
+    draft.attemptType = choose(allowableStyleMap[tickData.style ?? 'Lead'])
+    draft.source = 'OB'
+  })),
+  tick: async ({ ticks, tickData }, use) => await use(await ticks.addTick(tickData))
 })
 
-const testImport: TickInput[] = [
-  toTest, toTest2, tickUpdate
-]
-
 describe('Ticks', () => {
-  let ticks: TickDataSource
-  let climbs: MutableClimbDataSource
-  let areas: MutableAreaDataSource
-  const tickModel = getTickModel()
-
-  let users: UserDataSource
-
-  beforeAll(async () => {
-    console.log('#BeforeAll Ticks')
-    await inMemoryDB.connect()
-
-    try {
-      await getTickModel().collection.drop()
-      await getUserModel().collection.drop()
-    } catch (e) {
-      console.log('Cleaning db')
-    }
-
-    ticks = TickDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
-    users = UserDataSource.getInstance()
-    areas = MutableAreaDataSource.getInstance()
-    // Add climbs because add/update tick requires type validation
-    await areas.addCountry('usa')
-    const newDestination = await areas.addArea(userId, 'California', null, 'usa')
-    if (newDestination == null) fail('Expect new area to be created')
-
-    const routesArea = await areas.addArea(userId, 'Sport & Trad', newDestination.metadata.area_id)
-
-    const newIDs = await climbs.addOrUpdateClimbs(userId, routesArea.metadata.area_id, newClimbsToAdd)
-
-    // Update tick inputs with generated climb IDs
-    toTest.climbId = newIDs[0]
-    toTest2.climbId = newIDs[1]
-    tickUpdate = { ...tickUpdate, climbId: newIDs[0] } // Ensure tickUpdate has the correct climbId
-  })
-
-  afterAll(async () => {
-    await inMemoryDB.close()
-  })
-
-  afterEach(async () => {
-    await getTickModel().collection.drop()
-    await tickModel.ensureIndexes()
-  })
-
   // test adding tick
-  it('should create a new tick for the associated climb', async () => {
-    const tick = await ticks.addTick(toTest)
-    const newTick = await tickModel.findOne({ userId: toTest.userId })
+  it('should create a new tick for the associated climb', async ({ ticks, tickData }) => {
+    const tick = await ticks.addTick(tickData)
+    const newTick = await ticks.tickModel.findOne({ userId: tickData.userId })
     expect(newTick?._id).toEqual(tick._id)
   })
 
   // test updating tick
-  it('should update a tick and return the proper information', async () => {
-    const tick = await ticks.addTick(toTest)
+  it('should update a tick and return the proper information', async ({ ticks, tick, tickUpdateData }) => {
+    const newTick = await ticks.editTick({ _id: tick._id }, tickUpdateData)
 
-    if (tick == null) {
-      fail('Tick should not be null')
-    }
-    const newTick = await ticks.editTick({ _id: tick._id }, tickUpdate)
+    expect(newTick).not.toBeNull()
 
-    if (newTick == null) {
-      fail('The new tick should not be null')
-    }
     expect(newTick?._id).toEqual(tick._id)
-    expect(newTick?.notes).toEqual(tickUpdate.notes)
-    expect(newTick?.attemptType).toEqual(tickUpdate.attemptType)
+    expect(newTick?.notes).toEqual(tickUpdateData.notes)
+    expect(newTick?.attemptType).toEqual(tickUpdateData.attemptType)
   })
 
   // test removing tick
-  it('should remove a tick', async () => {
-    const tick = await ticks.addTick(toTest)
-
-    if (tick == null) {
-      fail('Tick should not be null')
-    }
+  it('should remove a tick', async ({ ticks, tick }) => {
     await ticks.deleteTick(tick._id)
-
-    const newTick = await tickModel.findOne({ _id: tick._id })
-
+    const newTick = await ticks.tickModel.findOne({ _id: tick._id })
     expect(newTick).toBeNull()
   })
 
   // test importing ticks
-  it('should add an array of ticks', async () => {
-    const newTicks = await ticks.importTicks(testImport)
+  it('should add an array of ticks', async ({ ticks, tickImportData }) => {
+    const newTicks = await ticks.importTicks(tickImportData)
 
-    if (newTicks == null) {
-      fail(`Should add ${testImport.length} new ticks`)
-    }
-    expect(newTicks?.length).toEqual(testImport.length)
+    expect(newTicks).not.toBeNull()
+    expect(newTicks).toHaveLength(tickImportData.length)
 
-    const tick1 = await tickModel.findOne({ _id: newTicks[0]._id })
+    const tick1 = await ticks.tickModel.findOne({ _id: newTicks[0]._id })
     expect(tick1?._id).toEqual(newTicks[0]._id)
 
-    const tick2 = await tickModel.findOne({ _id: newTicks[1]._id })
+    const tick2 = await ticks.tickModel.findOne({ _id: newTicks[1]._id })
     expect(tick2?._id).toEqual(newTicks[1]._id)
 
-    const tick3 = await tickModel.findOne({ _id: newTicks[2]._id })
+    const tick3 = await ticks.tickModel.findOne({ _id: newTicks[2]._id })
     expect(tick3?._id).toEqual(newTicks[2]._id)
   })
 
-  it('should grab all ticks by userId', async () => {
-    const userProfileInput: UpdateProfileGQLInput = {
-      userUuid: userId.toUUID().toString(),
-      username: 'cat.dog',
-      email: 'cat@example.com'
-    }
-    await users.createOrUpdateUserProfile(userId, userProfileInput)
-    const tick = await ticks.addTick(toTest)
-
-    if (tick == null) {
-      fail('Should add a new tick')
-    }
-
-    const newTicks = await ticks.ticksByUser({ userId })
-
-    expect(newTicks.length).toEqual(1)
+  it('should grab all ticks by userId', async ({ ticks, tick, userUuid, profile }) => {
+    const newTicks = await ticks.ticksByUser({ userId: profile._id })
+    newTicks.forEach(tick => expect(tick.userId).toEqual(userUuid))
+    expect(newTicks[0]._id.equals(tick._id))
   })
 
-  it('should grab all ticks by userId and climbId', async () => {
-    const climbId = toTest.climbId
-    const tick = await ticks.addTick(toTest)
-    const tick2 = await ticks.addTick(toTest2)
+  it('should grab all ticks by userId and climbId', async ({ ticks, tickImportData, climb, userUuid }) => {
+    await Promise.all([
+      ticks.addTick(tickImportData[0]),
+      ticks.addTick(tickImportData[1]),
+      ticks.addTick({ ...tickImportData[1], userId: muuidToString(muuid.v4()) })
+    ])
 
-    if (tick == null || tick2 == null) {
-      fail('Should add a new tick')
-    }
-    const userClimbTicks = await ticks.ticksByUserIdAndClimb(climbId, userId.toUUID().toString())
-    expect(userClimbTicks.length).toEqual(1)
+    const userClimbTicks = await ticks.ticksByUserIdAndClimb(muuidToString(climb._id), userUuid)
+    expect(userClimbTicks).toHaveLength(2)
   })
 
-  it('should delete all ticks with the specified userId', async () => {
-    const newTicks = await ticks.importTicks(testImport)
+  it('should delete all ticks with the specified userId', async ({ ticks, tickImportData, userUuid }) => {
+    const newTicks = await ticks.importTicks(tickImportData)
 
-    if (newTicks == null) {
-      fail('Should add 3 new ticks')
-    }
+    expect(newTicks).not.toBeNull()
+    expect(newTicks).toHaveLength(tickImportData.length)
 
-    await ticks.deleteAllTicks(userId.toUUID().toString())
-    const newTick = await tickModel.findOne({ userId })
+    await ticks.deleteAllTicks(userUuid)
+    const newTick = await ticks.tickModel.findOne({ userId: userUuid })
     expect(newTick).toBeNull()
   })
 
-  it('should only delete MP imports', async () => {
-    const MPTick = await ticks.addTick(toTest)
-    const OBTick = await ticks.addTick(tickUpdate)
-    if (MPTick == null || OBTick == null) {
-      fail('Should add two new ticks')
-    }
+  it('should only delete MP imports', async ({ ticks, tickData, tickUpdateData, userUuid }) => {
+    const MPTick = await ticks.addTick(tickData)
+    const OBTick = await ticks.addTick(tickUpdateData)
+
+    expect(MPTick).not.toBeNull()
+    expect(OBTick).not.toBeNull()
 
-    await ticks.deleteImportedTicks(userId.toUUID().toString())
-    const newTick = await tickModel.findOne({ _id: OBTick._id })
+    await ticks.deleteImportedTicks(userUuid)
+    const newTick = await ticks.tickModel.findOne({ _id: OBTick._id })
     expect(newTick?._id).toEqual(OBTick._id)
     expect(newTick?.notes).toEqual('Not sandbagged')
   })
diff --git a/src/model/__tests__/updateAreas.ts b/src/model/__tests__/updateAreas.ts
index b17df811..91109f91 100644
--- a/src/model/__tests__/updateAreas.ts
+++ b/src/model/__tests__/updateAreas.ts
@@ -1,132 +1,104 @@
 import muuid from 'uuid-mongodb'
 import { geometry } from '@turf/helpers'
-
-import MutableAreaDataSource from '../MutableAreaDataSource.js'
-import MutableClimbDataSource from '../MutableClimbDataSource.js'
-import { createIndexes, getAreaModel, getClimbModel } from '../../db/index.js'
+import countries from 'i18n-iso-countries'
 import { AreaEditableFieldsType, UpdateSortingOrderType } from '../../db/AreaTypes.js'
-import inMemoryDB from '../../utils/inMemoryDB.js'
+import { dataFixtures as it } from '../../__tests__/fixtures/data.fixtures'
+import { gradeContextToGradeScales } from '../../GradeUtils.js'
 
 describe('Areas', () => {
-  let areas: MutableAreaDataSource
-  let climbs: MutableClimbDataSource
-  const testUser = muuid.v4()
-
-  beforeAll(async () => {
-    await inMemoryDB.connect()
-
-    try {
-      await getAreaModel().collection.drop()
-      await getClimbModel().collection.drop()
-    } catch (e) {
-      console.log('Cleaning up db before test', e)
-    }
-    await createIndexes()
-    areas = MutableAreaDataSource.getInstance()
-    climbs = MutableClimbDataSource.getInstance()
+  it('should create a country by Alpha-3 country code', async ({ areas, countryCode }) => {
+    const country = await areas.addCountry(countryCode.toLocaleLowerCase())
+    const newArea = await areas.findOneAreaByUUID(country.metadata.area_id)
+    expect(newArea.area_name).toEqual(countries.getName(countryCode, 'en'))
+    expect(newArea.shortCode).toEqual(countryCode)
   })
 
-  afterAll(async () => {
-    await inMemoryDB.close()
+  it('should create a country by Alpha-2 country code', async ({ areas, countryCode }) => {
+    const alpha2 = countries.alpha3ToAlpha2(countryCode)
+    assert(alpha2)
+    const country = await areas.addCountry(alpha2)
+    expect(country.area_name).toEqual(countries.getName(countryCode, 'en'))
+    // should be expanded to the long country code
+    expect(country.shortCode).toEqual(countryCode)
   })
 
-  it('should create a country by Alpha-3 country code', async () => {
-    const spain = await areas.addCountry('esP')
-    const newArea = await areas.findOneAreaByUUID(spain.metadata.area_id)
-    expect(newArea.area_name).toEqual('Spain')
-    expect(newArea.shortCode).toEqual('ESP')
-  })
-
-  it('should create a country by Alpha-2 country code', async () => {
-    const country = await areas.addCountry('ch')
-    expect(country.area_name).toEqual('Switzerland')
-    expect(country.shortCode).toEqual('CHE')
-  })
-
-  it('should create a country and 2 subareas', async () => {
-    const canada = await areas.addCountry('can')
+  it('should create a country and 2 subareas', async ({ areas, user, countryCode }) => {
+    const country = await areas.addCountry(countryCode)
     // Add 1st area to the country
-    const bc = await areas.addArea(testUser, 'British Columbia', canada.metadata.area_id)
+    const district = await areas.addArea(user, 'British Columbia', country.metadata.area_id)
+    assert(district != null)
+    assert(country != null)
 
-    if (bc == null || canada == null) {
-      fail()
-    }
-    expect(canada.metadata.lnglat).not.toMatchObject(geometry('Point', [0, 0]))
-    expect(bc.area_name).toEqual('British Columbia')
+    expect(country.metadata.lnglat).not.toMatchObject(geometry('Point', [0, 0]))
+    expect(district.area_name).toEqual('British Columbia')
 
-    expect(bc.metadata.lnglat).toEqual(canada.metadata.lnglat)
+    expect(district.metadata.lnglat).toEqual(country.metadata.lnglat)
 
-    let canadaInDb = await areas.findOneAreaByUUID(canada.metadata.area_id)
+    let countryInDB = await areas.findOneAreaByUUID(country.metadata.area_id)
 
-    expect(canadaInDb.children.length).toEqual(1)
-    expect(canadaInDb.children[0]).toEqual(bc?._id)
+    expect(countryInDB.children.length).toEqual(1)
+    expect(countryInDB.children[0]).toEqual(district?._id)
 
     // Add another area to the country
-    const theBug = await areas.addArea(testUser, 'The Bugaboos', canada.metadata.area_id)
+    const province = await areas.addArea(user, 'The Bugaboos', country.metadata.area_id)
 
-    canadaInDb = await areas.findOneAreaByUUID(canada.metadata.area_id)
-    expect(canadaInDb.children.length).toEqual(2)
-    expect(canadaInDb.children[1]).toEqual(theBug?._id)
+    countryInDB = await areas.findOneAreaByUUID(country.metadata.area_id)
+    expect(countryInDB.children.length).toEqual(2)
+    expect(countryInDB.children[1]).toEqual(province?._id)
 
     // Verify paths and ancestors
-    if (theBug != null) { // make TS happy
-      expect(theBug.ancestors)
-        .toEqual(`${canada.metadata.area_id.toUUID().toString()},${theBug?.metadata.area_id.toUUID().toString()}`)
-      expect(theBug.pathTokens)
-        .toEqual([canada.area_name, theBug.area_name])
-    }
+    assert(province !== null)
+
+    expect(province.ancestors)
+      .toEqual(`${country.metadata.area_id.toUUID().toString()},${province?.metadata.area_id.toUUID().toString()}`)
+    expect(province.pathTokens)
+      .toEqual([country.area_name, province.area_name])
   })
 
-  it('should allow adding child areas to empty leaf area', async () => {
-    let parent = await areas.addArea(testUser, 'My house', null, 'can')
-    await areas.updateArea(testUser, parent.metadata.area_id, { isLeaf: true, isBoulder: true })
+  it('should allow adding child areas to empty leaf area', async ({ areas, user, climbs, country, area }) => {
+    await areas.updateArea(user, area.metadata.area_id, { isLeaf: true, isBoulder: true })
 
-    const newClimb = await climbs.addOrUpdateClimbs(testUser, parent.metadata.area_id, [{ name: 'Big Mac' }])
+    gradeContextToGradeScales[country.gradeContext] = gradeContextToGradeScales.US
+    const newClimb = await climbs.addOrUpdateClimbs(user, area.metadata.area_id, [{ name: 'Big Mac' }])
 
     // Try to add a new area when there's already a climb
-    await expect(areas.addArea(testUser, 'Kitchen', parent.metadata.area_id)).rejects.toThrow(/Adding new areas to a leaf or boulder area is not allowed/)
+    await expect(areas.addArea(user, 'Kitchen', area.metadata.area_id)).rejects.toThrow('Adding new areas to a leaf or boulder area is not allowed')
 
     // Now remove the climb to see if we can add the area
+    await climbs.deleteClimbs(user, area.metadata.area_id, [muuid.from(newClimb[0])])
+    await areas.addArea(user, 'Kitchen', area.metadata.area_id)
 
-    await climbs.deleteClimbs(testUser, parent.metadata.area_id, [muuid.from(newClimb[0])])
-    await areas.addArea(testUser, 'Kitchen', parent.metadata.area_id)
+    // Reload the parent area
+    area = await areas.findOneAreaByUUID(area.metadata.area_id)
 
-    // Reload the parent
-    parent = await areas.findOneAreaByUUID(parent.metadata.area_id)
-    expect(parent.climbs).toHaveLength(0)
-    expect(parent.children).toHaveLength(1)
+    expect(area.climbs).toHaveLength(0)
+    expect(area.children).toHaveLength(1)
     // make sure leaf and boulder flag are cleared
-    expect(parent.metadata.leaf).toBeFalsy()
-    expect(parent.metadata.isBoulder).toBeFalsy()
+    expect(area.metadata.leaf).toBeFalsy()
+    expect(area.metadata.isBoulder).toBeFalsy()
   })
 
-  it('should create an area using only country code (without parent id)', async () => {
-    const country = await areas.addCountry('za')
-    const area = await areas.addArea(testUser, 'Table mountain', null, 'zaf')
+  it('should create an area using only country code (without parent id)', async ({ areas, user, countryCode }) => {
+    const country = await areas.addCountry(countryCode)
+    const area = await areas.addArea(user, 'Table mountain', null, countryCode)
 
     const countryInDb = await areas.findOneAreaByUUID(country.metadata.area_id)
     expect(countryInDb.children.length).toEqual(1)
     expect(countryInDb.children[0]).toEqual(area?._id)
   })
 
-  it('should set crag/boulder attribute when adding new areas', async () => {
-    let parent = await areas.addArea(testUser, 'Boulder A', null, 'can', undefined, false, true)
+  it('should set crag/boulder attribute when adding new areas', async ({ areas, user, country }) => {
+    let parent = await areas.addArea(user, 'Boulder A', country.metadata.area_id, undefined, undefined, false, true)
     expect(parent.metadata.isBoulder).toBe(true)
     expect(parent.metadata.leaf).toBe(true)
 
-    parent = await areas.addArea(testUser, 'Sport A', null, 'can', undefined, true, undefined)
+    parent = await areas.addArea(user, 'Sport A', country.metadata.area_id, undefined, undefined, true, undefined)
     expect(parent.metadata.isBoulder).toBe(false)
     expect(parent.metadata.leaf).toBe(true)
   })
 
-  it('should update multiple fields', async () => {
-    await areas.addCountry('au')
-    const a1 = await areas.addArea(testUser, 'One', null, 'au')
-
-    if (a1 == null) {
-      fail()
-    }
-    // for testing area description and location is sanitized
+  it('should update multiple fields', async ({ areas, user, area }) => {
+    // for testing area desccription is sanitized
     const iframeStr = '<iframe src="https://www.googlecom" title="Evil Iframe"></iframe>'
     const doc1: AreaEditableFieldsType = {
       areaName: '1',
@@ -135,7 +107,7 @@ describe('Areas', () => {
       areaLocation: `This is a cool area location with some malicious code.${iframeStr}`,
       isDestination: true
     }
-    let a1Updated = await areas.updateArea(testUser, a1?.metadata.area_id, doc1)
+    let a1Updated = await areas.updateArea(user, area?.metadata.area_id, doc1)
 
     expect(a1Updated?.area_name).toEqual(doc1.areaName)
     expect(a1Updated?.shortCode).toEqual(doc1.shortCode)
@@ -150,36 +122,28 @@ describe('Areas', () => {
       lat: 46.433333,
       lng: 11.85
     }
-    a1Updated = await areas.updateArea(testUser, a1?.metadata.area_id, doc2)
+    a1Updated = await areas.updateArea(user, area?.metadata.area_id, doc2)
     expect(a1Updated?.metadata.lnglat).toEqual(geometry('Point', [doc2.lng, doc2.lat]))
     expect(a1Updated?.metadata.isDestination).toEqual(doc2.isDestination)
   })
 
-  it('should not update country name and code', async () => {
-    const country = await areas.addCountry('lao')
-    if (country == null) fail()
-    await expect(areas.updateArea(testUser, country.metadata.area_id, { areaName: 'Foo' })).rejects.toThrowError()
-
-    // eslint-disable-next-line
-    await new Promise(res => setTimeout(res, 2000))
-
-    await expect(areas.updateArea(testUser, country.metadata.area_id, { shortCode: 'Foo' })).rejects.toThrowError()
+  it('should not update country name and code', async ({ areas, user, country }) => {
+    await expect(areas.updateArea(user, country.metadata.area_id, { areaName: 'Foo' })).rejects.toThrowError()
   })
 
-  it('should delete a subarea', async () => {
-    const usa = await areas.addCountry('usa')
-    const ca = await areas.addArea(testUser, 'CA', usa.metadata.area_id)
-    const or = await areas.addArea(testUser, 'OR', usa.metadata.area_id)
-    const wa = await areas.addArea(testUser, 'WA', usa.metadata.area_id)
+  it('should delete a subarea', async ({ areas, user, country }) => {
+    const ca = await areas.addArea(user, 'CA', country.metadata.area_id)
+    const or = await areas.addArea(user, 'OR', country.metadata.area_id)
+    const wa = await areas.addArea(user, 'WA', country.metadata.area_id)
 
-    if (ca == null || or == null || wa == null) {
-      fail('Child area is null')
-    }
+    assert(ca != null, 'child area is null')
+    assert(or != null, 'child area is null')
+    assert(wa != null, 'child area is null')
 
-    // eslint-disable-next-line
-    await new Promise(res => setTimeout(res, 3000))
+    //
+    // await new Promise(res => setTimeout(res, 3000))
 
-    let usaInDB = await areas.findOneAreaByUUID(usa.metadata.area_id)
+    let usaInDB = await areas.findOneAreaByUUID(country.metadata.area_id)
     // verify number of child areas in parent
     expect(usaInDB.children as any[]).toHaveLength(3)
 
@@ -190,9 +154,9 @@ describe('Areas', () => {
       wa._id
     ])
 
-    await areas.deleteArea(testUser, ca.metadata.area_id)
+    await areas.deleteArea(user, ca.metadata.area_id)
 
-    usaInDB = await areas.findOneAreaByUUID(usa.metadata.area_id)
+    usaInDB = await areas.findOneAreaByUUID(country.metadata.area_id)
 
     // verify child area IDs (one less than before)
     expect(usaInDB.children as any[]).toHaveLength(2)
@@ -204,60 +168,60 @@ describe('Areas', () => {
     await expect(areas.findOneAreaByUUID(ca.metadata.area_id)).rejects.toThrow(/Area.*not found/)
   })
 
-  it('should not delete a subarea containing children', async () => {
-    const gr = await areas.addCountry('grc')
-    const kali = await areas.addArea(testUser, 'Kalymnos', gr.metadata.area_id)
+  it('should not delete a subarea containing children', async ({ areas, user, countryCode }) => {
+    const country = await areas.addCountry(countryCode)
+    const province = await areas.addArea(user, 'Kalymnos', country.metadata.area_id)
 
-    if (kali == null) fail()
+    assert(province != null)
 
-    const arhi = await areas.addArea(testUser, 'Arhi', kali.metadata.area_id)
+    const arhi = await areas.addArea(user, 'Arhi', province.metadata.area_id)
 
-    if (arhi == null) fail()
+    assert(arhi != null)
 
     // Try to delete 'Arhi' (expecting exception)
-    await expect(areas.deleteArea(testUser, kali.metadata.area_id)).rejects.toThrow('subareas not empty')
+    await expect(areas.deleteArea(user, province.metadata.area_id)).rejects.toThrow('subareas not empty')
 
     const arhiInDb = await areas.findOneAreaByUUID(arhi.metadata.area_id)
     expect(arhiInDb._id).toEqual(arhi._id)
   })
 
-  it('should not create duplicate countries', async () => {
-    await areas.addCountry('ita')
+  it('should not create duplicate countries', async ({ areas, user, countryCode }) => {
+    await areas.addCountry(countryCode)
 
     // eslint-disable-next-line
     await new Promise(res => setTimeout(res, 2000))
 
-    await expect(areas.addCountry('ita')).rejects.toThrowError('This name already exists for some other area in this parent')
+    await expect(areas.addCountry(countryCode)).rejects.toThrowError('This name already exists for some other area in this parent')
   })
 
-  it('should not create duplicate sub-areas', async () => {
-    const fr = await areas.addCountry('fra')
-    await areas.addArea(testUser, 'Verdon Gorge', fr.metadata.area_id)
-    await expect(areas.addArea(testUser, 'Verdon Gorge', fr.metadata.area_id))
+  it('should not create duplicate sub-areas', async ({ areas, user, countryCode }) => {
+    const country = await areas.addCountry(countryCode)
+    await areas.addArea(user, 'Verdon Gorge', country.metadata.area_id)
+    await expect(areas.addArea(user, 'Verdon Gorge', country.metadata.area_id))
       .rejects.toThrowError('This name already exists for some other area in this parent')
   })
 
-  it('should fail when adding without a parent country', async () => {
-    await expect(areas.addArea(testUser, 'Peak District ', null, 'GB'))
+  it('should fail when adding without a parent country', async ({ areas, user, climbs }) => {
+    await expect(areas.addArea(user, 'Peak District ', null, 'GB'))
       .rejects.toThrowError()
   })
 
-  it('should fail when adding with a non-existent parent id', async () => {
+  it('should fail when adding with a non-existent parent id', async ({ areas, user, climbs }) => {
     const notInDb = muuid.from('abf6cb8b-8461-45c3-b46b-5997444be867')
-    await expect(areas.addArea(testUser, 'Land\'s End ', notInDb))
+    await expect(areas.addArea(user, 'Land\'s End ', notInDb))
       .rejects.toThrowError()
   })
 
-  it('should fail when adding with null parents', async () => {
-    await expect(areas.addArea(testUser, 'Land\'s End ', null, '1q1'))
+  it('should fail when adding with null parents', async ({ areas, user, climbs }) => {
+    await expect(areas.addArea(user, 'Land\'s End ', null, '1q1'))
       .rejects.toThrowError()
   })
 
-  it('should update areas sorting order', async () => {
+  it('should update areas sorting order', async ({ areas, user, climbs }) => {
     // Setup
     await areas.addCountry('MX')
-    const a1 = await areas.addArea(testUser, 'A1', null, 'MX')
-    const a2 = await areas.addArea(testUser, 'A2', null, 'MX')
+    const a1 = await areas.addArea(user, 'A1', null, 'MX')
+    const a2 = await areas.addArea(user, 'A2', null, 'MX')
 
     const change1: UpdateSortingOrderType = {
       areaId: a1.metadata.area_id.toUUID().toString(),
@@ -269,7 +233,7 @@ describe('Areas', () => {
     }
 
     // Update
-    await areas.updateSortingOrder(testUser, [change1, change2])
+    await areas.updateSortingOrder(user, [change1, change2])
 
     // Verify
     const a1Actual = await areas.findOneAreaByUUID(a1.metadata.area_id)
@@ -291,15 +255,15 @@ describe('Areas', () => {
       }))
   })
 
-  it('should update self and childrens pathTokens', async () => {
+  it('should update self and childrens pathTokens', async ({ areas, user, climbs }) => {
     await areas.addCountry('JP')
-    const a1 = await areas.addArea(testUser, 'Parent', null, 'JP')
-    const b1 = await areas.addArea(testUser, 'B1', a1.metadata.area_id)
-    const b2 = await areas.addArea(testUser, 'B2', a1.metadata.area_id)
-    const c1 = await areas.addArea(testUser, 'C1', b1.metadata.area_id)
-    const c2 = await areas.addArea(testUser, 'C2', b1.metadata.area_id)
-    const c3 = await areas.addArea(testUser, 'C3', b2.metadata.area_id)
-    const e1 = await areas.addArea(testUser, 'E1', c3.metadata.area_id)
+    const a1 = await areas.addArea(user, 'Parent', null, 'JP')
+    const b1 = await areas.addArea(user, 'B1', a1.metadata.area_id)
+    const b2 = await areas.addArea(user, 'B2', a1.metadata.area_id)
+    const c1 = await areas.addArea(user, 'C1', b1.metadata.area_id)
+    const c2 = await areas.addArea(user, 'C2', b1.metadata.area_id)
+    const c3 = await areas.addArea(user, 'C3', b2.metadata.area_id)
+    const e1 = await areas.addArea(user, 'E1', c3.metadata.area_id)
 
     let a1Actual = await areas.findOneAreaByUUID(a1.metadata.area_id)
     expect(a1Actual).toEqual(
@@ -348,7 +312,7 @@ describe('Areas', () => {
     const doc1: AreaEditableFieldsType = {
       areaName: 'Test Name'
     }
-    await areas.updateArea(testUser, a1?.metadata.area_id, doc1)
+    await areas.updateArea(user, a1?.metadata.area_id, doc1)
 
     // Verify
     a1Actual = await areas.findOneAreaByUUID(a1.metadata.area_id)
diff --git a/src/utils/inMemoryDB.ts b/src/utils/inMemoryDB.ts
deleted file mode 100644
index f30e8eef..00000000
--- a/src/utils/inMemoryDB.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import mongoose, { ConnectOptions } from 'mongoose'
-import { ChangeStream, ChangeStreamDocument, MongoClient } from 'mongodb'
-import { MongoMemoryReplSet } from 'mongodb-memory-server'
-import { checkVar, defaultPostConnect } from '../db/index.js'
-import { logger } from '../logger.js'
-import { testStreamListener } from '../db/edit/streamListener'
-
-/**
- * In-memory Mongo replset used for testing.
- * More portable than requiring user to set up Mongo in a background Docker process.
- * Need a replset to faciliate transactions.
- */
-let mongod: MongoMemoryReplSet
-let stream: ChangeStream | undefined
-
-/**
- * Connect to the in-memory database.
- */
-export const connect = async (onChange?: (change: ChangeStreamDocument) => void): Promise<void> => {
-  mongod = await MongoMemoryReplSet.create({
-    // Stream listener listens on DB denoted by 'MONGO_DBNAME' env var.
-    replSet: { count: 1, storageEngine: 'wiredTiger', dbName: checkVar('MONGO_DBNAME') }
-  })
-  const uri = await mongod.getUri(checkVar('MONGO_DBNAME'))
-  logger.info(`Connecting to in-memory database ${uri}`)
-  const mongooseOpts: ConnectOptions = {
-    autoIndex: false // Create indices using defaultPostConnect instead.
-  }
-
-  await mongoose.connect(uri, mongooseOpts)
-  mongoose.set('debug', false) // Set to 'true' to enable verbose mode
-  stream = await defaultPostConnect(async () => await testStreamListener(onChange))
-}
-
-/**
- * Drop database, close the connection and stop mongod.
- */
-export const close = async (): Promise<void> => {
-  await stream?.close()
-  await mongoose.connection.dropDatabase()
-  await mongoose.connection.close()
-  await mongod.stop()
-}
-
-/**
- * Remove all the data for all db collections.
- */
-export const clear = async (): Promise<void> => {
-  const collections = mongoose.connection.collections
-
-  for (const key in collections) {
-    const collection = collections[key]
-    await collection.deleteMany({})
-  }
-}
-
-/**
- * Bypass Mongoose to insert data directly into Mongo.
- * Useful for inserting data that is incompatible with Mongoose schemas for migration testing.
- * @param collection Name of collection for documents to be inserted into.
- * @param docs Documents to be inserted into collection.
- */
-const insertDirectly = async (collection: string, documents: any[]): Promise<void> => {
-  const uri = await mongod.getUri(checkVar('MONGO_DBNAME'))
-  const client = new MongoClient(uri)
-  try {
-    const database = client.db(checkVar('MONGO_DBNAME'))
-    const mCollection = database.collection(collection)
-    const result = await mCollection.insertMany(documents)
-
-    console.log(`${result.insertedCount} documents were inserted directly into MongoDB`)
-  } finally {
-    void client.close()
-  }
-}
-
-export interface InMemoryDB {
-  connect: () => Promise<void>
-  close: () => Promise<void>
-  clear: () => Promise<void>
-  insertDirectly: (collection: string, documents: any[]) => Promise<void>
-}
-
-export default { connect, close, clear, insertDirectly, stream }
diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts
index 6006cccf..e49da079 100644
--- a/src/utils/testUtils.ts
+++ b/src/utils/testUtils.ts
@@ -1,79 +1,3 @@
-import jwt from 'jsonwebtoken'
-import { jest } from '@jest/globals'
-import request from 'supertest'
-import { ApolloServer } from '@apollo/server'
-import express from 'express'
-
-import type { InMemoryDB } from './inMemoryDB.js'
-import inMemoryDB from './inMemoryDB.js'
-import { createServer } from '../server.js'
-
-const PORT = 4000
-
-export interface QueryAPIProps {
-  query?: string
-  operationName?: string
-  variables?: any
-  userUuid?: string
-  roles?: string[]
-  port?: number
-  endpoint?: string
-  app?: express.Application
-  body?: any
-}
-
-/*
- * Helper function for querying the locally-served API. It mocks JWT verification
- * so we can pretend to have an role we want when calling the API.
- */
-export const queryAPI = async ({
-  query,
-  operationName,
-  variables,
-  userUuid = '',
-  roles = [],
-  app,
-  endpoint = '/',
-  port = PORT
-}: QueryAPIProps): Promise<request.Response> => {
-  // Avoid needing to pass in actual signed tokens.
-  const jwtSpy = jest.spyOn(jwt, 'verify')
-  jwtSpy.mockImplementation(() => {
-    return {
-      // Roles defined at https://manage.auth0.com/dashboard/us/dev-fmjy7n5n/roles
-      'https://tacos.openbeta.io/roles': roles,
-      'https://tacos.openbeta.io/uuid': userUuid
-    }
-  })
-
-  const queryObj = { query, operationName, variables }
-  let req = request(app ?? `http://localhost:${port}`)
-    .post(endpoint)
-    .send(queryObj)
-
-  if (userUuid != null) {
-    req = req.set('Authorization', 'Bearer placeholder-jwt-see-SpyOn')
-  }
-
-  return await req
-}
-
-export interface SetUpServerReturnType {
-  server: ApolloServer
-  app: express.Application
-  inMemoryDB: InMemoryDB
-}
-
-/*
- * Starts Apollo server and has Mongo inMemory replset connect to it.
-*/
-export const setUpServer = async (): Promise<SetUpServerReturnType> => {
-  await inMemoryDB.connect()
-
-  const { app, server } = await createServer()
-  return { app, server, inMemoryDB }
-}
-
 export const isFulfilled = <T>(
   p: PromiseSettledResult<T>
 ): p is PromiseFulfilledResult<T> => p.status === 'fulfilled'
diff --git a/tsconfig.json b/tsconfig.json
index 3b503295..e8439192 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
   "compilerOptions": {
     "types": [
       "node",
-      "jest",
+      "vitest/globals",
     ],
     "allowJs": true,
     "skipLibCheck": true,
@@ -19,5 +19,6 @@
   },
   "include": [
     "src/**/*.ts",
+    "vite.config.ts"
   ]
 }
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 00000000..e6888958
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig } from 'vitest/config'
+export default defineConfig({
+  test: {
+    include: ['src/**/*.test.ts', 'src/**/__tests__/**/*.ts'],
+    exclude: ['**/*/fixtures'],
+    globals: true,
+    environment: 'node',
+
+    pool: 'threads',
+    poolOptions: {
+      threads: {
+        isolate: false
+      }
+    }
+  }
+})
diff --git a/yarn.lock b/yarn.lock
index c19a69ad..538f494e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,14 +2,6 @@
 # yarn lockfile v1
 
 
-"@ampproject/remapping@^2.2.0":
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
-  integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
-  dependencies:
-    "@jridgewell/gen-mapping" "^0.3.5"
-    "@jridgewell/trace-mapping" "^0.3.24"
-
 "@apollo/cache-control-types@^1.0.3":
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz#5da62cf64c3b4419dabfef4536b57a40c8ff0b47"
@@ -168,234 +160,6 @@
   resolved "https://registry.yarnpkg.com/@apollo/utils.withrequired/-/utils.withrequired-2.0.1.tgz#e72bc512582a6f26af150439f7eb7473b46ba874"
   integrity sha512-YBDiuAX9i1lLc6GeTy1m7DGLFn/gMnvXqlalOIMjM7DeOgIacEjjfwPqb0M1CQ2v11HhR15d1NmxJoRCfrNqcA==
 
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
-  version "7.26.2"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
-  integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
-  dependencies:
-    "@babel/helper-validator-identifier" "^7.25.9"
-    js-tokens "^4.0.0"
-    picocolors "^1.0.0"
-
-"@babel/compat-data@^7.25.9":
-  version "7.26.2"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e"
-  integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==
-
-"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9":
-  version "7.26.0"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
-  integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
-  dependencies:
-    "@ampproject/remapping" "^2.2.0"
-    "@babel/code-frame" "^7.26.0"
-    "@babel/generator" "^7.26.0"
-    "@babel/helper-compilation-targets" "^7.25.9"
-    "@babel/helper-module-transforms" "^7.26.0"
-    "@babel/helpers" "^7.26.0"
-    "@babel/parser" "^7.26.0"
-    "@babel/template" "^7.25.9"
-    "@babel/traverse" "^7.25.9"
-    "@babel/types" "^7.26.0"
-    convert-source-map "^2.0.0"
-    debug "^4.1.0"
-    gensync "^1.0.0-beta.2"
-    json5 "^2.2.3"
-    semver "^6.3.1"
-
-"@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2":
-  version "7.26.2"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f"
-  integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==
-  dependencies:
-    "@babel/parser" "^7.26.2"
-    "@babel/types" "^7.26.0"
-    "@jridgewell/gen-mapping" "^0.3.5"
-    "@jridgewell/trace-mapping" "^0.3.25"
-    jsesc "^3.0.2"
-
-"@babel/helper-compilation-targets@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875"
-  integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==
-  dependencies:
-    "@babel/compat-data" "^7.25.9"
-    "@babel/helper-validator-option" "^7.25.9"
-    browserslist "^4.24.0"
-    lru-cache "^5.1.1"
-    semver "^6.3.1"
-
-"@babel/helper-module-imports@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
-  integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
-  dependencies:
-    "@babel/traverse" "^7.25.9"
-    "@babel/types" "^7.25.9"
-
-"@babel/helper-module-transforms@^7.26.0":
-  version "7.26.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
-  integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
-  dependencies:
-    "@babel/helper-module-imports" "^7.25.9"
-    "@babel/helper-validator-identifier" "^7.25.9"
-    "@babel/traverse" "^7.25.9"
-
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46"
-  integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==
-
-"@babel/helper-string-parser@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
-  integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
-
-"@babel/helper-validator-identifier@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
-  integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
-
-"@babel/helper-validator-option@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
-  integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
-
-"@babel/helpers@^7.26.0":
-  version "7.26.0"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
-  integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==
-  dependencies:
-    "@babel/template" "^7.25.9"
-    "@babel/types" "^7.26.0"
-
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2":
-  version "7.26.2"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11"
-  integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==
-  dependencies:
-    "@babel/types" "^7.26.0"
-
-"@babel/plugin-syntax-async-generators@^7.8.4":
-  version "7.8.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
-  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-bigint@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
-  integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-class-properties@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
-  integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-syntax-class-static-block@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
-  integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-import-attributes@^7.24.7":
-  version "7.26.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7"
-  integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.25.9"
-
-"@babel/plugin-syntax-import-meta@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
-  integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-json-strings@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
-  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-jsx@^7.7.2":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290"
-  integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.25.9"
-
-"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
-  integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
-  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-numeric-separator@^7.10.4":
-  version "7.10.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
-  integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-object-rest-spread@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
-  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
-  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-chaining@^7.8.3":
-  version "7.8.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
-  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-private-property-in-object@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
-  integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-top-level-await@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
-  integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-typescript@^7.7.2":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399"
-  integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.25.9"
-
 "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.2", "@babel/runtime@^7.21.0":
   version "7.26.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
@@ -403,40 +167,120 @@
   dependencies:
     regenerator-runtime "^0.14.0"
 
-"@babel/template@^7.25.9", "@babel/template@^7.3.3":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016"
-  integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==
-  dependencies:
-    "@babel/code-frame" "^7.25.9"
-    "@babel/parser" "^7.25.9"
-    "@babel/types" "^7.25.9"
-
-"@babel/traverse@^7.25.9":
-  version "7.25.9"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
-  integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
-  dependencies:
-    "@babel/code-frame" "^7.25.9"
-    "@babel/generator" "^7.25.9"
-    "@babel/parser" "^7.25.9"
-    "@babel/template" "^7.25.9"
-    "@babel/types" "^7.25.9"
-    debug "^4.3.1"
-    globals "^11.1.0"
-
-"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3":
-  version "7.26.0"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
-  integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
-  dependencies:
-    "@babel/helper-string-parser" "^7.25.9"
-    "@babel/helper-validator-identifier" "^7.25.9"
-
-"@bcoe/v8-coverage@^0.2.3":
-  version "0.2.3"
-  resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
-  integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+"@esbuild/aix-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
+  integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
+
+"@esbuild/android-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
+  integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
+
+"@esbuild/android-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
+  integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
+
+"@esbuild/android-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
+  integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
+
+"@esbuild/darwin-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
+  integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
+
+"@esbuild/darwin-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
+  integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
+
+"@esbuild/freebsd-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
+  integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
+
+"@esbuild/freebsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
+  integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
+
+"@esbuild/linux-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
+  integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
+
+"@esbuild/linux-arm@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
+  integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
+
+"@esbuild/linux-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
+  integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
+
+"@esbuild/linux-loong64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
+  integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
+
+"@esbuild/linux-mips64el@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
+  integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
+
+"@esbuild/linux-ppc64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
+  integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
+
+"@esbuild/linux-riscv64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
+  integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
+
+"@esbuild/linux-s390x@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
+  integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
+
+"@esbuild/linux-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
+  integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
+
+"@esbuild/netbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
+  integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
+
+"@esbuild/openbsd-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
+  integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
+
+"@esbuild/sunos-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
+  integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
+
+"@esbuild/win32-arm64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
+  integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
+
+"@esbuild/win32-ia32@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
+  integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
+
+"@esbuild/win32-x64@0.21.5":
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
+  integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
 
 "@eslint-community/eslint-utils@^4.2.0":
   version "4.4.1"
@@ -621,246 +465,11 @@
     wrap-ansi "^8.1.0"
     wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
 
-"@istanbuljs/load-nyc-config@^1.0.0":
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
-  integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
-  dependencies:
-    camelcase "^5.3.1"
-    find-up "^4.1.0"
-    get-package-type "^0.1.0"
-    js-yaml "^3.13.1"
-    resolve-from "^5.0.0"
-
-"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3":
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
-  integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
-
-"@jest/console@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
-  integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    jest-message-util "^29.7.0"
-    jest-util "^29.7.0"
-    slash "^3.0.0"
-
-"@jest/core@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
-  integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
-  dependencies:
-    "@jest/console" "^29.7.0"
-    "@jest/reporters" "^29.7.0"
-    "@jest/test-result" "^29.7.0"
-    "@jest/transform" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    ansi-escapes "^4.2.1"
-    chalk "^4.0.0"
-    ci-info "^3.2.0"
-    exit "^0.1.2"
-    graceful-fs "^4.2.9"
-    jest-changed-files "^29.7.0"
-    jest-config "^29.7.0"
-    jest-haste-map "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-regex-util "^29.6.3"
-    jest-resolve "^29.7.0"
-    jest-resolve-dependencies "^29.7.0"
-    jest-runner "^29.7.0"
-    jest-runtime "^29.7.0"
-    jest-snapshot "^29.7.0"
-    jest-util "^29.7.0"
-    jest-validate "^29.7.0"
-    jest-watcher "^29.7.0"
-    micromatch "^4.0.4"
-    pretty-format "^29.7.0"
-    slash "^3.0.0"
-    strip-ansi "^6.0.0"
-
-"@jest/environment@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
-  integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
-  dependencies:
-    "@jest/fake-timers" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    jest-mock "^29.7.0"
-
-"@jest/expect-utils@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
-  integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
-  dependencies:
-    jest-get-type "^29.6.3"
-
-"@jest/expect@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
-  integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
-  dependencies:
-    expect "^29.7.0"
-    jest-snapshot "^29.7.0"
-
-"@jest/fake-timers@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
-  integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    "@sinonjs/fake-timers" "^10.0.2"
-    "@types/node" "*"
-    jest-message-util "^29.7.0"
-    jest-mock "^29.7.0"
-    jest-util "^29.7.0"
-
-"@jest/globals@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
-  integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
-  dependencies:
-    "@jest/environment" "^29.7.0"
-    "@jest/expect" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    jest-mock "^29.7.0"
-
-"@jest/reporters@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
-  integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
-  dependencies:
-    "@bcoe/v8-coverage" "^0.2.3"
-    "@jest/console" "^29.7.0"
-    "@jest/test-result" "^29.7.0"
-    "@jest/transform" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@jridgewell/trace-mapping" "^0.3.18"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    collect-v8-coverage "^1.0.0"
-    exit "^0.1.2"
-    glob "^7.1.3"
-    graceful-fs "^4.2.9"
-    istanbul-lib-coverage "^3.0.0"
-    istanbul-lib-instrument "^6.0.0"
-    istanbul-lib-report "^3.0.0"
-    istanbul-lib-source-maps "^4.0.0"
-    istanbul-reports "^3.1.3"
-    jest-message-util "^29.7.0"
-    jest-util "^29.7.0"
-    jest-worker "^29.7.0"
-    slash "^3.0.0"
-    string-length "^4.0.1"
-    strip-ansi "^6.0.0"
-    v8-to-istanbul "^9.0.1"
-
-"@jest/schemas@^29.6.3":
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
-  integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
-  dependencies:
-    "@sinclair/typebox" "^0.27.8"
-
-"@jest/source-map@^29.6.3":
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
-  integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
-  dependencies:
-    "@jridgewell/trace-mapping" "^0.3.18"
-    callsites "^3.0.0"
-    graceful-fs "^4.2.9"
-
-"@jest/test-result@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
-  integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
-  dependencies:
-    "@jest/console" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/istanbul-lib-coverage" "^2.0.0"
-    collect-v8-coverage "^1.0.0"
-
-"@jest/test-sequencer@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
-  integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
-  dependencies:
-    "@jest/test-result" "^29.7.0"
-    graceful-fs "^4.2.9"
-    jest-haste-map "^29.7.0"
-    slash "^3.0.0"
-
-"@jest/transform@^29.7.0":
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
-  integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
-  dependencies:
-    "@babel/core" "^7.11.6"
-    "@jest/types" "^29.6.3"
-    "@jridgewell/trace-mapping" "^0.3.18"
-    babel-plugin-istanbul "^6.1.1"
-    chalk "^4.0.0"
-    convert-source-map "^2.0.0"
-    fast-json-stable-stringify "^2.1.0"
-    graceful-fs "^4.2.9"
-    jest-haste-map "^29.7.0"
-    jest-regex-util "^29.6.3"
-    jest-util "^29.7.0"
-    micromatch "^4.0.4"
-    pirates "^4.0.4"
-    slash "^3.0.0"
-    write-file-atomic "^4.0.2"
-
-"@jest/types@^29.6.3":
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
-  integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
-  dependencies:
-    "@jest/schemas" "^29.6.3"
-    "@types/istanbul-lib-coverage" "^2.0.0"
-    "@types/istanbul-reports" "^3.0.0"
-    "@types/node" "*"
-    "@types/yargs" "^17.0.8"
-    chalk "^4.0.0"
-
-"@jridgewell/gen-mapping@^0.3.5":
-  version "0.3.5"
-  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
-  integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
-  dependencies:
-    "@jridgewell/set-array" "^1.2.1"
-    "@jridgewell/sourcemap-codec" "^1.4.10"
-    "@jridgewell/trace-mapping" "^0.3.24"
-
-"@jridgewell/resolve-uri@^3.1.0":
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
-  integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-
-"@jridgewell/set-array@^1.2.1":
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
-  integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
-
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+"@jridgewell/sourcemap-codec@^1.5.0":
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
   integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
 
-"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
-  version "0.3.25"
-  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
-  integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
-  dependencies:
-    "@jridgewell/resolve-uri" "^3.1.0"
-    "@jridgewell/sourcemap-codec" "^1.4.14"
-
 "@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.1.9":
   version "1.1.9"
   resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz#e974bab8eca9faa88677d4ea4da8d09a52069004"
@@ -957,30 +566,106 @@
   resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
   integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
 
+"@rollup/rollup-android-arm-eabi@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz#7f4c4d8cd5ccab6e95d6750dbe00321c1f30791e"
+  integrity sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==
+
+"@rollup/rollup-android-arm64@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz#17ea71695fb1518c2c324badbe431a0bd1879f2d"
+  integrity sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==
+
+"@rollup/rollup-darwin-arm64@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz#dac0f0d0cfa73e7d5225ae6d303c13c8979e7999"
+  integrity sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==
+
+"@rollup/rollup-darwin-x64@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz#8f63baa1d31784904a380d2e293fa1ddf53dd4a2"
+  integrity sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==
+
+"@rollup/rollup-freebsd-arm64@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz#30ed247e0df6e8858cdc6ae4090e12dbeb8ce946"
+  integrity sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==
+
+"@rollup/rollup-freebsd-x64@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz#57846f382fddbb508412ae07855b8a04c8f56282"
+  integrity sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==
+
+"@rollup/rollup-linux-arm-gnueabihf@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz#378ca666c9dae5e6f94d1d351e7497c176e9b6df"
+  integrity sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==
+
+"@rollup/rollup-linux-arm-musleabihf@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz#a692eff3bab330d5c33a5d5813a090c15374cddb"
+  integrity sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==
+
+"@rollup/rollup-linux-arm64-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz#6b1719b76088da5ac1ae1feccf48c5926b9e3db9"
+  integrity sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==
+
+"@rollup/rollup-linux-arm64-musl@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz#865baf5b6f5ff67acb32e5a359508828e8dc5788"
+  integrity sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==
+
+"@rollup/rollup-linux-loongarch64-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz#23c6609ba0f7fa7a7f2038b6b6a08555a5055a87"
+  integrity sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==
+
+"@rollup/rollup-linux-powerpc64le-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz#652ef0d9334a9f25b9daf85731242801cb0fc41c"
+  integrity sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==
+
+"@rollup/rollup-linux-riscv64-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz#1eb6651839ee6ebca64d6cc64febbd299e95e6bd"
+  integrity sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==
+
+"@rollup/rollup-linux-s390x-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz#015c52293afb3ff2a293cf0936b1d43975c1e9cd"
+  integrity sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==
+
+"@rollup/rollup-linux-x64-gnu@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz#b83001b5abed2bcb5e2dbeec6a7e69b194235c1e"
+  integrity sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==
+
+"@rollup/rollup-linux-x64-musl@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz#6cc7c84cd4563737f8593e66f33b57d8e228805b"
+  integrity sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==
+
+"@rollup/rollup-win32-arm64-msvc@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz#631ffeee094d71279fcd1fe8072bdcf25311bc11"
+  integrity sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==
+
+"@rollup/rollup-win32-ia32-msvc@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz#06d1d60d5b9f718e8a6c4a43f82e3f9e3254587f"
+  integrity sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==
+
+"@rollup/rollup-win32-x64-msvc@4.28.1":
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz#4dff5c4259ebe6c5b4a8f2c5bc3829b7a8447ff0"
+  integrity sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==
+
 "@rtsao/scc@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
   integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
 
-"@sinclair/typebox@^0.27.8":
-  version "0.27.8"
-  resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
-  integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
-
-"@sinonjs/commons@^3.0.0":
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
-  integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==
-  dependencies:
-    type-detect "4.0.8"
-
-"@sinonjs/fake-timers@^10.0.2":
-  version "10.3.0"
-  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
-  integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
-  dependencies:
-    "@sinonjs/commons" "^3.0.0"
-
 "@tootallnate/once@2":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
@@ -1058,39 +743,6 @@
   resolved "https://registry.yarnpkg.com/@types/auth0/-/auth0-3.3.10.tgz#6c483d7ebe6e3eb1fb44d5c27aecbe49ad0b200d"
   integrity sha512-9tS0Y2igWxw+Dx5uCHkIUCu6tG0oRkwpE322dOJPwZMLXQMx49n/gDmUz7YJSe1iVjrWW+ffVYmlPShVIEwjkg==
 
-"@types/babel__core@^7.1.14":
-  version "7.20.5"
-  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
-  integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
-  dependencies:
-    "@babel/parser" "^7.20.7"
-    "@babel/types" "^7.20.7"
-    "@types/babel__generator" "*"
-    "@types/babel__template" "*"
-    "@types/babel__traverse" "*"
-
-"@types/babel__generator@*":
-  version "7.6.8"
-  resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab"
-  integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==
-  dependencies:
-    "@babel/types" "^7.0.0"
-
-"@types/babel__template@*":
-  version "7.4.4"
-  resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
-  integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
-  dependencies:
-    "@babel/parser" "^7.1.0"
-    "@babel/types" "^7.0.0"
-
-"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
-  version "7.20.6"
-  resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7"
-  integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==
-  dependencies:
-    "@babel/types" "^7.20.7"
-
 "@types/body-parser@*":
   version "1.19.5"
   resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
@@ -1111,6 +763,11 @@
   resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78"
   integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==
 
+"@types/estree@1.0.6", "@types/estree@^1.0.0":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
+  integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
 "@types/express-serve-static-core@^4.17.30", "@types/express-serve-static-core@^4.17.33":
   version "4.19.6"
   resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267"
@@ -1131,45 +788,11 @@
     "@types/qs" "*"
     "@types/serve-static" "*"
 
-"@types/graceful-fs@^4.1.3":
-  version "4.1.9"
-  resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
-  integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==
-  dependencies:
-    "@types/node" "*"
-
 "@types/http-errors@*":
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
   integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
 
-"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
-  integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
-
-"@types/istanbul-lib-report@*":
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf"
-  integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==
-  dependencies:
-    "@types/istanbul-lib-coverage" "*"
-
-"@types/istanbul-reports@^3.0.0":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
-  integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
-  dependencies:
-    "@types/istanbul-lib-report" "*"
-
-"@types/jest@^29.4.0":
-  version "29.5.14"
-  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5"
-  integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
-  dependencies:
-    expect "^29.0.0"
-    pretty-format "^29.0.0"
-
 "@types/json-schema@^7.0.9":
   version "7.0.15"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
@@ -1268,11 +891,6 @@
     "@types/node" "*"
     "@types/send" "*"
 
-"@types/stack-utils@^2.0.0":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
-  integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
-
 "@types/superagent@*":
   version "8.1.9"
   resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.9.tgz#28bfe4658e469838ed0bf66d898354bcab21f49f"
@@ -1320,18 +938,6 @@
     "@types/node" "*"
     "@types/webidl-conversions" "*"
 
-"@types/yargs-parser@*":
-  version "21.0.3"
-  resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
-  integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
-
-"@types/yargs@^17.0.8":
-  version "17.0.33"
-  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d"
-  integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==
-  dependencies:
-    "@types/yargs-parser" "*"
-
 "@types/yup@0.29.13":
   version "0.29.13"
   resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.13.tgz#21b137ba60841307a3c8a1050d3bf4e63ad561e9"
@@ -1426,6 +1032,65 @@
   resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
   integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
 
+"@vitest/expect@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1"
+  integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==
+  dependencies:
+    "@vitest/spy" "2.1.8"
+    "@vitest/utils" "2.1.8"
+    chai "^5.1.2"
+    tinyrainbow "^1.2.0"
+
+"@vitest/mocker@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73"
+  integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==
+  dependencies:
+    "@vitest/spy" "2.1.8"
+    estree-walker "^3.0.3"
+    magic-string "^0.30.12"
+
+"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca"
+  integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==
+  dependencies:
+    tinyrainbow "^1.2.0"
+
+"@vitest/runner@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f"
+  integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==
+  dependencies:
+    "@vitest/utils" "2.1.8"
+    pathe "^1.1.2"
+
+"@vitest/snapshot@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de"
+  integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==
+  dependencies:
+    "@vitest/pretty-format" "2.1.8"
+    magic-string "^0.30.12"
+    pathe "^1.1.2"
+
+"@vitest/spy@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447"
+  integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==
+  dependencies:
+    tinyspy "^3.0.2"
+
+"@vitest/utils@2.1.8":
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388"
+  integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==
+  dependencies:
+    "@vitest/pretty-format" "2.1.8"
+    loupe "^3.1.2"
+    tinyrainbow "^1.2.0"
+
 abort-controller@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@@ -1475,13 +1140,6 @@ ajv@^6.12.4:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-ansi-escapes@^4.2.1:
-  version "4.3.2"
-  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
-  integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
-  dependencies:
-    type-fest "^0.21.3"
-
 ansi-regex@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -1499,24 +1157,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
-ansi-styles@^5.0.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
-  integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
-
 ansi-styles@^6.1.0:
   version "6.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
   integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
 
-anymatch@^3.0.3:
-  version "3.1.3"
-  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
-  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
-  dependencies:
-    normalize-path "^3.0.0"
-    picomatch "^2.0.4"
-
 apollo-datasource-mongodb@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/apollo-datasource-mongodb/-/apollo-datasource-mongodb-0.6.0.tgz#643e3311cff1861a11cc967eff7259d0a84342e3"
@@ -1526,13 +1171,6 @@ apollo-datasource-mongodb@^0.6.0:
     bson "^5.4.0"
     dataloader "^1.4.0"
 
-argparse@^1.0.7:
-  version "1.0.10"
-  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
-  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
-  dependencies:
-    sprintf-js "~1.0.2"
-
 argparse@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -1647,6 +1285,11 @@ asap@^2.0.0:
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
 
+assertion-error@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
+  integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
+
 async-mutex@^0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.5.0.tgz#353c69a0b9e75250971a64ac203b0ebfddd75482"
@@ -1661,11 +1304,6 @@ async-retry@^1.2.1, async-retry@^1.3.3:
   dependencies:
     retry "0.13.1"
 
-async@^3.2.3:
-  version "3.2.6"
-  resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
-  integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
-
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1711,69 +1349,6 @@ b4a@^1.6.4:
   resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4"
   integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==
 
-babel-jest@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
-  integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
-  dependencies:
-    "@jest/transform" "^29.7.0"
-    "@types/babel__core" "^7.1.14"
-    babel-plugin-istanbul "^6.1.1"
-    babel-preset-jest "^29.6.3"
-    chalk "^4.0.0"
-    graceful-fs "^4.2.9"
-    slash "^3.0.0"
-
-babel-plugin-istanbul@^6.1.1:
-  version "6.1.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
-  integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.0.0"
-    "@istanbuljs/load-nyc-config" "^1.0.0"
-    "@istanbuljs/schema" "^0.1.2"
-    istanbul-lib-instrument "^5.0.4"
-    test-exclude "^6.0.0"
-
-babel-plugin-jest-hoist@^29.6.3:
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
-  integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
-  dependencies:
-    "@babel/template" "^7.3.3"
-    "@babel/types" "^7.3.3"
-    "@types/babel__core" "^7.1.14"
-    "@types/babel__traverse" "^7.0.6"
-
-babel-preset-current-node-syntax@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30"
-  integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==
-  dependencies:
-    "@babel/plugin-syntax-async-generators" "^7.8.4"
-    "@babel/plugin-syntax-bigint" "^7.8.3"
-    "@babel/plugin-syntax-class-properties" "^7.12.13"
-    "@babel/plugin-syntax-class-static-block" "^7.14.5"
-    "@babel/plugin-syntax-import-attributes" "^7.24.7"
-    "@babel/plugin-syntax-import-meta" "^7.10.4"
-    "@babel/plugin-syntax-json-strings" "^7.8.3"
-    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
-    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
-    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
-    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
-    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
-    "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
-    "@babel/plugin-syntax-top-level-await" "^7.14.5"
-
-babel-preset-jest@^29.6.3:
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
-  integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
-  dependencies:
-    babel-plugin-jest-hoist "^29.6.3"
-    babel-preset-current-node-syntax "^1.0.0"
-
 balanced-match@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -1878,30 +1453,6 @@ braces@^3.0.3:
   dependencies:
     fill-range "^7.1.1"
 
-browserslist@^4.24.0:
-  version "4.24.2"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
-  integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
-  dependencies:
-    caniuse-lite "^1.0.30001669"
-    electron-to-chromium "^1.5.41"
-    node-releases "^2.0.18"
-    update-browserslist-db "^1.1.1"
-
-bs-logger@^0.2.6:
-  version "0.2.6"
-  resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
-  integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
-  dependencies:
-    fast-json-stable-stringify "2.x"
-
-bser@2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
-  integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
-  dependencies:
-    node-int64 "^0.4.0"
-
 bson@^5.4.0, bson@^5.5.0:
   version "5.5.1"
   resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.1.tgz#f5849d405711a7f23acdda9a442375df858e6833"
@@ -1922,11 +1473,6 @@ buffer-equal-constant-time@1.0.1:
   resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
   integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
 
-buffer-from@^1.0.0:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
-  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-
 buffer@^5.5.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -1947,6 +1493,11 @@ bytes@3.1.2:
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
   integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
 
+cac@^6.7.14:
+  version "6.7.14"
+  resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
+  integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
+
 call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
@@ -1971,22 +1522,23 @@ camel-case@^1.1.1:
     sentence-case "^1.1.1"
     upper-case "^1.1.1"
 
-camelcase@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
-  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
-camelcase@^6.2.0, camelcase@^6.3.0:
+camelcase@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
 
-caniuse-lite@^1.0.30001669:
-  version "1.0.30001684"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz#0eca437bab7d5f03452ff0ef9de8299be6b08e16"
-  integrity sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==
+chai@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
+  integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
+  dependencies:
+    assertion-error "^2.0.1"
+    check-error "^2.1.1"
+    deep-eql "^5.0.1"
+    loupe "^3.1.0"
+    pathval "^2.0.0"
 
-chalk@^4.0.0, chalk@^4.0.2:
+chalk@^4.0.0:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -2016,45 +1568,16 @@ change-case@^2.3.0:
     upper-case "^1.1.1"
     upper-case-first "^1.1.0"
 
-char-regex@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
-  integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+check-error@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
+  integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
 
 chownr@^1.1.1:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
   integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
 
-ci-info@^3.2.0:
-  version "3.9.0"
-  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
-  integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
-
-cjs-module-lexer@^1.0.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170"
-  integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==
-
-cliui@^8.0.1:
-  version "8.0.1"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
-  integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
-  dependencies:
-    string-width "^4.2.0"
-    strip-ansi "^6.0.1"
-    wrap-ansi "^7.0.0"
-
-co@^4.6.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
-  integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==
-
-collect-v8-coverage@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9"
-  integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==
-
 color-convert@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
@@ -2152,11 +1675,6 @@ content-type@~1.0.4, content-type@~1.0.5:
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
   integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
 
-convert-source-map@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
-  integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
-
 cookie-signature@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -2180,19 +1698,6 @@ cors@^2.8.5:
     object-assign "^4"
     vary "^1"
 
-create-jest@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
-  integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    chalk "^4.0.0"
-    exit "^0.1.2"
-    graceful-fs "^4.2.9"
-    jest-config "^29.7.0"
-    jest-util "^29.7.0"
-    prompts "^2.0.1"
-
 cross-env@^7.0.3:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
@@ -2200,7 +1705,7 @@ cross-env@^7.0.3:
   dependencies:
     cross-spawn "^7.0.1"
 
-cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2:
   version "7.0.6"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
   integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
@@ -2281,10 +1786,10 @@ decompress-response@^6.0.0:
   dependencies:
     mimic-response "^3.1.0"
 
-dedent@^1.0.0:
-  version "1.5.3"
-  resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a"
-  integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==
+deep-eql@^5.0.1:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
+  integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
 
 deep-extend@^0.6.0:
   version "0.6.0"
@@ -2344,11 +1849,6 @@ detect-libc@^2.0.0, detect-libc@^2.0.2:
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
   integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
 
-detect-newline@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
-  integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
-
 dezalgo@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
@@ -2362,11 +1862,6 @@ diacritics@1.3.0:
   resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1"
   integrity sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==
 
-diff-sequences@^29.6.3:
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
-  integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
-
 dir-glob@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -2465,23 +1960,6 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
 
-ejs@^3.1.10:
-  version "3.1.10"
-  resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
-  integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==
-  dependencies:
-    jake "^10.8.5"
-
-electron-to-chromium@^1.5.41:
-  version "1.5.66"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.66.tgz#1e9b4bc7638ac02d3551eea1dbaeb0101ec5823f"
-  integrity sha512-pI2QF6+i+zjPbqRzJwkMvtvkdI7MjVbSh2g8dlMguDJIXEPw+kwasS1Jl+YGPEBfGVxsVgGUratAKymPdPo2vQ==
-
-emittery@^0.13.1:
-  version "0.13.1"
-  resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
-  integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
-
 emoji-regex@^8.0.0:
   version "8.0.0"
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -2613,6 +2091,11 @@ es-iterator-helpers@^1.1.0:
     iterator.prototype "^1.1.3"
     safe-array-concat "^1.1.2"
 
+es-module-lexer@^1.5.4:
+  version "1.5.4"
+  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78"
+  integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==
+
 es-object-atoms@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
@@ -2645,21 +2128,40 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.5"
     is-symbol "^1.0.4"
 
-escalade@^3.1.1, escalade@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
-  integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+esbuild@^0.21.3:
+  version "0.21.5"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
+  integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
+  optionalDependencies:
+    "@esbuild/aix-ppc64" "0.21.5"
+    "@esbuild/android-arm" "0.21.5"
+    "@esbuild/android-arm64" "0.21.5"
+    "@esbuild/android-x64" "0.21.5"
+    "@esbuild/darwin-arm64" "0.21.5"
+    "@esbuild/darwin-x64" "0.21.5"
+    "@esbuild/freebsd-arm64" "0.21.5"
+    "@esbuild/freebsd-x64" "0.21.5"
+    "@esbuild/linux-arm" "0.21.5"
+    "@esbuild/linux-arm64" "0.21.5"
+    "@esbuild/linux-ia32" "0.21.5"
+    "@esbuild/linux-loong64" "0.21.5"
+    "@esbuild/linux-mips64el" "0.21.5"
+    "@esbuild/linux-ppc64" "0.21.5"
+    "@esbuild/linux-riscv64" "0.21.5"
+    "@esbuild/linux-s390x" "0.21.5"
+    "@esbuild/linux-x64" "0.21.5"
+    "@esbuild/netbsd-x64" "0.21.5"
+    "@esbuild/openbsd-x64" "0.21.5"
+    "@esbuild/sunos-x64" "0.21.5"
+    "@esbuild/win32-arm64" "0.21.5"
+    "@esbuild/win32-ia32" "0.21.5"
+    "@esbuild/win32-x64" "0.21.5"
 
 escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
   integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
 
-escape-string-regexp@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
-  integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
-
 escape-string-regexp@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
@@ -2873,11 +2375,6 @@ espree@^9.6.0, espree@^9.6.1:
     acorn-jsx "^5.3.2"
     eslint-visitor-keys "^3.4.1"
 
-esprima@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
-  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
 esquery@^1.4.2:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
@@ -2902,6 +2399,13 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
   integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
 
+estree-walker@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
+  integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
+  dependencies:
+    "@types/estree" "^1.0.0"
+
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -2917,41 +2421,15 @@ event-target-shim@^5.0.0:
   resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
   integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
 
-execa@^5.0.0:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
-  integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
-  dependencies:
-    cross-spawn "^7.0.3"
-    get-stream "^6.0.0"
-    human-signals "^2.1.0"
-    is-stream "^2.0.0"
-    merge-stream "^2.0.0"
-    npm-run-path "^4.0.1"
-    onetime "^5.1.2"
-    signal-exit "^3.0.3"
-    strip-final-newline "^2.0.0"
-
-exit@^0.1.2:
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
-  integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==
-
 expand-template@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
   integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
 
-expect@^29.0.0, expect@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
-  integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
-  dependencies:
-    "@jest/expect-utils" "^29.7.0"
-    jest-get-type "^29.6.3"
-    jest-matcher-utils "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-util "^29.7.0"
+expect-type@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
+  integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==
 
 express@^4.18.2, express@^4.21.1:
   version "4.21.1"
@@ -3021,7 +2499,7 @@ fast-json-parse@^1.0.3:
   resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d"
   integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==
 
-fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
+fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
   integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
@@ -3060,13 +2538,6 @@ fastq@^1.6.0:
   dependencies:
     reusify "^1.0.4"
 
-fb-watchman@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c"
-  integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==
-  dependencies:
-    bser "2.1.1"
-
 file-entry-cache@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -3074,13 +2545,6 @@ file-entry-cache@^6.0.1:
   dependencies:
     flat-cache "^3.0.4"
 
-filelist@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
-  integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
-  dependencies:
-    minimatch "^5.0.1"
-
 fill-range@^7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
@@ -3117,7 +2581,7 @@ find-up@^3.0.0:
   dependencies:
     locate-path "^3.0.0"
 
-find-up@^4.0.0, find-up@^4.1.0:
+find-up@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
   integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
@@ -3228,7 +2692,7 @@ fs.realpath@^1.0.0:
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
   integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
 
-fsevents@^2.3.2:
+fsevents@~2.3.2, fsevents@~2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
   integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
@@ -3271,16 +2735,6 @@ gcp-metadata@^5.3.0:
     gaxios "^5.0.0"
     json-bigint "^1.0.0"
 
-gensync@^1.0.0-beta.2:
-  version "1.0.0-beta.2"
-  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
-  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-
-get-caller-file@^2.0.5:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
-  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-
 get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
@@ -3292,21 +2746,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
     has-symbols "^1.0.3"
     hasown "^2.0.0"
 
-get-package-type@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
-  integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
-
 get-stdin@^8.0.0:
   version "8.0.0"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
   integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
 
-get-stream@^6.0.0:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
-  integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
-
 get-symbol-description@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5"
@@ -3347,7 +2791,7 @@ glob@^10.2.2:
     package-json-from-dist "^1.0.0"
     path-scurry "^1.11.1"
 
-glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@^7.1.3, glob@^7.1.6:
   version "7.2.3"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
   integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -3359,11 +2803,6 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-globals@^11.1.0:
-  version "11.12.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
-  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-
 globals@^13.19.0:
   version "13.24.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
@@ -3420,7 +2859,7 @@ gopd@^1.0.1:
   dependencies:
     get-intrinsic "^1.1.3"
 
-graceful-fs@^4.1.15, graceful-fs@^4.2.9:
+graceful-fs@^4.1.15:
   version "4.2.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -3518,12 +2957,7 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
 hexoid@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
-  integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
-
-html-escaper@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
-  integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+  integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
 
 htmlparser2@^8.0.0:
   version "8.0.2"
@@ -3571,11 +3005,6 @@ https-proxy-agent@^7.0.5:
     agent-base "^7.0.2"
     debug "4"
 
-human-signals@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
-  integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-
 husky@^8.0.1:
   version "8.0.3"
   resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
@@ -3618,14 +3047,6 @@ import-fresh@^3.2.1:
     parent-module "^1.0.0"
     resolve-from "^4.0.0"
 
-import-local@^3.0.2:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260"
-  integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==
-  dependencies:
-    pkg-dir "^4.2.0"
-    resolve-cwd "^3.0.0"
-
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -3754,11 +3175,6 @@ is-fullwidth-code-point@^3.0.0:
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
 
-is-generator-fn@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
-  integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
-
 is-generator-function@^1.0.10:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
@@ -3895,59 +3311,6 @@ isexe@^2.0.0:
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
   integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
 
-istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
-  version "3.2.2"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
-  integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
-
-istanbul-lib-instrument@^5.0.4:
-  version "5.2.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
-  integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==
-  dependencies:
-    "@babel/core" "^7.12.3"
-    "@babel/parser" "^7.14.7"
-    "@istanbuljs/schema" "^0.1.2"
-    istanbul-lib-coverage "^3.2.0"
-    semver "^6.3.0"
-
-istanbul-lib-instrument@^6.0.0:
-  version "6.0.3"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765"
-  integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==
-  dependencies:
-    "@babel/core" "^7.23.9"
-    "@babel/parser" "^7.23.9"
-    "@istanbuljs/schema" "^0.1.3"
-    istanbul-lib-coverage "^3.2.0"
-    semver "^7.5.4"
-
-istanbul-lib-report@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
-  integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
-  dependencies:
-    istanbul-lib-coverage "^3.0.0"
-    make-dir "^4.0.0"
-    supports-color "^7.1.0"
-
-istanbul-lib-source-maps@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
-  integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
-  dependencies:
-    debug "^4.1.1"
-    istanbul-lib-coverage "^3.0.0"
-    source-map "^0.6.1"
-
-istanbul-reports@^3.1.3:
-  version "3.1.7"
-  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b"
-  integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==
-  dependencies:
-    html-escaper "^2.0.0"
-    istanbul-lib-report "^3.0.0"
-
 iterator.prototype@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c"
@@ -3968,382 +3331,6 @@ jackspeak@^3.1.2:
   optionalDependencies:
     "@pkgjs/parseargs" "^0.11.0"
 
-jake@^10.8.5:
-  version "10.9.2"
-  resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
-  integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==
-  dependencies:
-    async "^3.2.3"
-    chalk "^4.0.2"
-    filelist "^1.0.4"
-    minimatch "^3.1.2"
-
-jest-changed-files@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
-  integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
-  dependencies:
-    execa "^5.0.0"
-    jest-util "^29.7.0"
-    p-limit "^3.1.0"
-
-jest-circus@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
-  integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
-  dependencies:
-    "@jest/environment" "^29.7.0"
-    "@jest/expect" "^29.7.0"
-    "@jest/test-result" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    co "^4.6.0"
-    dedent "^1.0.0"
-    is-generator-fn "^2.0.0"
-    jest-each "^29.7.0"
-    jest-matcher-utils "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-runtime "^29.7.0"
-    jest-snapshot "^29.7.0"
-    jest-util "^29.7.0"
-    p-limit "^3.1.0"
-    pretty-format "^29.7.0"
-    pure-rand "^6.0.0"
-    slash "^3.0.0"
-    stack-utils "^2.0.3"
-
-jest-cli@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
-  integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
-  dependencies:
-    "@jest/core" "^29.7.0"
-    "@jest/test-result" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    chalk "^4.0.0"
-    create-jest "^29.7.0"
-    exit "^0.1.2"
-    import-local "^3.0.2"
-    jest-config "^29.7.0"
-    jest-util "^29.7.0"
-    jest-validate "^29.7.0"
-    yargs "^17.3.1"
-
-jest-config@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
-  integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
-  dependencies:
-    "@babel/core" "^7.11.6"
-    "@jest/test-sequencer" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    babel-jest "^29.7.0"
-    chalk "^4.0.0"
-    ci-info "^3.2.0"
-    deepmerge "^4.2.2"
-    glob "^7.1.3"
-    graceful-fs "^4.2.9"
-    jest-circus "^29.7.0"
-    jest-environment-node "^29.7.0"
-    jest-get-type "^29.6.3"
-    jest-regex-util "^29.6.3"
-    jest-resolve "^29.7.0"
-    jest-runner "^29.7.0"
-    jest-util "^29.7.0"
-    jest-validate "^29.7.0"
-    micromatch "^4.0.4"
-    parse-json "^5.2.0"
-    pretty-format "^29.7.0"
-    slash "^3.0.0"
-    strip-json-comments "^3.1.1"
-
-jest-diff@^29.0.0, jest-diff@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
-  integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
-  dependencies:
-    chalk "^4.0.0"
-    diff-sequences "^29.6.3"
-    jest-get-type "^29.6.3"
-    pretty-format "^29.7.0"
-
-jest-docblock@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
-  integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
-  dependencies:
-    detect-newline "^3.0.0"
-
-jest-each@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
-  integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    chalk "^4.0.0"
-    jest-get-type "^29.6.3"
-    jest-util "^29.7.0"
-    pretty-format "^29.7.0"
-
-jest-environment-node@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
-  integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
-  dependencies:
-    "@jest/environment" "^29.7.0"
-    "@jest/fake-timers" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    jest-mock "^29.7.0"
-    jest-util "^29.7.0"
-
-jest-extended@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-4.0.2.tgz#d23b52e687cedf66694e6b2d77f65e211e99e021"
-  integrity sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==
-  dependencies:
-    jest-diff "^29.0.0"
-    jest-get-type "^29.0.0"
-
-jest-get-type@^29.0.0, jest-get-type@^29.6.3:
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
-  integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
-
-jest-haste-map@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
-  integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    "@types/graceful-fs" "^4.1.3"
-    "@types/node" "*"
-    anymatch "^3.0.3"
-    fb-watchman "^2.0.0"
-    graceful-fs "^4.2.9"
-    jest-regex-util "^29.6.3"
-    jest-util "^29.7.0"
-    jest-worker "^29.7.0"
-    micromatch "^4.0.4"
-    walker "^1.0.8"
-  optionalDependencies:
-    fsevents "^2.3.2"
-
-jest-leak-detector@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
-  integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
-  dependencies:
-    jest-get-type "^29.6.3"
-    pretty-format "^29.7.0"
-
-jest-matcher-utils@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
-  integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
-  dependencies:
-    chalk "^4.0.0"
-    jest-diff "^29.7.0"
-    jest-get-type "^29.6.3"
-    pretty-format "^29.7.0"
-
-jest-message-util@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
-  integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
-  dependencies:
-    "@babel/code-frame" "^7.12.13"
-    "@jest/types" "^29.6.3"
-    "@types/stack-utils" "^2.0.0"
-    chalk "^4.0.0"
-    graceful-fs "^4.2.9"
-    micromatch "^4.0.4"
-    pretty-format "^29.7.0"
-    slash "^3.0.0"
-    stack-utils "^2.0.3"
-
-jest-mock@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
-  integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    jest-util "^29.7.0"
-
-jest-pnp-resolver@^1.2.2:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e"
-  integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==
-
-jest-regex-util@^29.6.3:
-  version "29.6.3"
-  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
-  integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
-
-jest-resolve-dependencies@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
-  integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
-  dependencies:
-    jest-regex-util "^29.6.3"
-    jest-snapshot "^29.7.0"
-
-jest-resolve@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
-  integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
-  dependencies:
-    chalk "^4.0.0"
-    graceful-fs "^4.2.9"
-    jest-haste-map "^29.7.0"
-    jest-pnp-resolver "^1.2.2"
-    jest-util "^29.7.0"
-    jest-validate "^29.7.0"
-    resolve "^1.20.0"
-    resolve.exports "^2.0.0"
-    slash "^3.0.0"
-
-jest-runner@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
-  integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
-  dependencies:
-    "@jest/console" "^29.7.0"
-    "@jest/environment" "^29.7.0"
-    "@jest/test-result" "^29.7.0"
-    "@jest/transform" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    emittery "^0.13.1"
-    graceful-fs "^4.2.9"
-    jest-docblock "^29.7.0"
-    jest-environment-node "^29.7.0"
-    jest-haste-map "^29.7.0"
-    jest-leak-detector "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-resolve "^29.7.0"
-    jest-runtime "^29.7.0"
-    jest-util "^29.7.0"
-    jest-watcher "^29.7.0"
-    jest-worker "^29.7.0"
-    p-limit "^3.1.0"
-    source-map-support "0.5.13"
-
-jest-runtime@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
-  integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
-  dependencies:
-    "@jest/environment" "^29.7.0"
-    "@jest/fake-timers" "^29.7.0"
-    "@jest/globals" "^29.7.0"
-    "@jest/source-map" "^29.6.3"
-    "@jest/test-result" "^29.7.0"
-    "@jest/transform" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    cjs-module-lexer "^1.0.0"
-    collect-v8-coverage "^1.0.0"
-    glob "^7.1.3"
-    graceful-fs "^4.2.9"
-    jest-haste-map "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-mock "^29.7.0"
-    jest-regex-util "^29.6.3"
-    jest-resolve "^29.7.0"
-    jest-snapshot "^29.7.0"
-    jest-util "^29.7.0"
-    slash "^3.0.0"
-    strip-bom "^4.0.0"
-
-jest-snapshot@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
-  integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
-  dependencies:
-    "@babel/core" "^7.11.6"
-    "@babel/generator" "^7.7.2"
-    "@babel/plugin-syntax-jsx" "^7.7.2"
-    "@babel/plugin-syntax-typescript" "^7.7.2"
-    "@babel/types" "^7.3.3"
-    "@jest/expect-utils" "^29.7.0"
-    "@jest/transform" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    babel-preset-current-node-syntax "^1.0.0"
-    chalk "^4.0.0"
-    expect "^29.7.0"
-    graceful-fs "^4.2.9"
-    jest-diff "^29.7.0"
-    jest-get-type "^29.6.3"
-    jest-matcher-utils "^29.7.0"
-    jest-message-util "^29.7.0"
-    jest-util "^29.7.0"
-    natural-compare "^1.4.0"
-    pretty-format "^29.7.0"
-    semver "^7.5.3"
-
-jest-util@^29.0.0, jest-util@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
-  integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    chalk "^4.0.0"
-    ci-info "^3.2.0"
-    graceful-fs "^4.2.9"
-    picomatch "^2.2.3"
-
-jest-validate@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
-  integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
-  dependencies:
-    "@jest/types" "^29.6.3"
-    camelcase "^6.2.0"
-    chalk "^4.0.0"
-    jest-get-type "^29.6.3"
-    leven "^3.1.0"
-    pretty-format "^29.7.0"
-
-jest-watcher@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
-  integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
-  dependencies:
-    "@jest/test-result" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    "@types/node" "*"
-    ansi-escapes "^4.2.1"
-    chalk "^4.0.0"
-    emittery "^0.13.1"
-    jest-util "^29.7.0"
-    string-length "^4.0.1"
-
-jest-worker@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
-  integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
-  dependencies:
-    "@types/node" "*"
-    jest-util "^29.7.0"
-    merge-stream "^2.0.0"
-    supports-color "^8.0.0"
-
-jest@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
-  integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
-  dependencies:
-    "@jest/core" "^29.7.0"
-    "@jest/types" "^29.6.3"
-    import-local "^3.0.2"
-    jest-cli "^29.7.0"
-
 jose@^2.0.6:
   version "2.0.7"
   resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.7.tgz#3aabbaec70bff313c108b9406498a163737b16ba"
@@ -4356,19 +3343,11 @@ jose@^4.14.6:
   resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100"
   integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==
 
-"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+"js-tokens@^3.0.0 || ^4.0.0":
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
   integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
 
-js-yaml@^3.13.1:
-  version "3.14.1"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
-  integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
-  dependencies:
-    argparse "^1.0.7"
-    esprima "^4.0.0"
-
 js-yaml@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -4381,11 +3360,6 @@ jsbn@1.1.0:
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
   integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
 
-jsesc@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
-  integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
-
 json-bigint@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
@@ -4403,11 +3377,6 @@ json-parse-better-errors@^1.0.1:
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
 
-json-parse-even-better-errors@^2.3.0:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
-  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
-
 json-schema-traverse@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -4430,11 +3399,6 @@ json5@^1.0.2:
   dependencies:
     minimist "^1.2.0"
 
-json5@^2.2.3:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
-  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
-
 jsonwebtoken@^8.5.1:
   version "8.5.1"
   resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
@@ -4547,16 +3511,6 @@ keyv@^4.5.3:
   dependencies:
     json-buffer "3.0.1"
 
-kleur@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
-  integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
-
-leven@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
-  integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
-
 levn@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -4570,11 +3524,6 @@ limiter@^1.1.5:
   resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2"
   integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==
 
-lines-and-columns@^1.1.6:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
-  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-
 load-json-file@^5.2.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3"
@@ -4665,11 +3614,6 @@ lodash.isstring@^4.0.1:
   resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
   integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
 
-lodash.memoize@^4.1.2:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
-  integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
-
 lodash.merge@^4.6.2:
   version "4.6.2"
   resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -4712,6 +3656,11 @@ loose-envify@^1.4.0:
   dependencies:
     js-tokens "^3.0.0 || ^4.0.0"
 
+loupe@^3.1.0, loupe@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
+  integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
+
 lower-case-first@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
@@ -4736,13 +3685,6 @@ lru-cache@^10.0.0, lru-cache@^10.2.0:
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
 
-lru-cache@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
-  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
-  dependencies:
-    yallist "^3.0.2"
-
 lru-cache@^7.10.1, lru-cache@^7.14.1:
   version "7.18.3"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
@@ -4756,6 +3698,13 @@ lru-memoizer@^2.1.4, lru-memoizer@^2.2.0:
     lodash.clonedeep "^4.5.0"
     lru-cache "6.0.0"
 
+magic-string@^0.30.12:
+  version "0.30.15"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.15.tgz#d5474a2c4c5f35f041349edaba8a5cb02733ed3c"
+  integrity sha512-zXeaYRgZ6ldS1RJJUrMrYgNJ4fdwnyI6tVqoiIhyCyv5IVTK9BU8Ic2l253GGETQHxI4HNUwhJ3fjDhKqEoaAw==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
 make-dir@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -4763,25 +3712,6 @@ make-dir@^3.0.2:
   dependencies:
     semver "^6.0.0"
 
-make-dir@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
-  integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
-  dependencies:
-    semver "^7.5.3"
-
-make-error@^1.3.6:
-  version "1.3.6"
-  resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
-  integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
-
-makeerror@1.0.12:
-  version "1.0.12"
-  resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
-  integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
-  dependencies:
-    tmpl "1.0.5"
-
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4797,11 +3727,6 @@ merge-descriptors@1.0.3:
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
   integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
 
-merge-stream@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
-  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-
 merge2@^1.3.0, merge2@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -4852,30 +3777,18 @@ mime@^3.0.0:
   resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
   integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
 
-mimic-fn@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
-  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-
 mimic-response@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
   integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
 
-minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
   dependencies:
     brace-expansion "^1.1.7"
 
-minimatch@^5.0.1:
-  version "5.1.6"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
-  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
-  dependencies:
-    brace-expansion "^2.0.1"
-
 minimatch@^9.0.4:
   version "9.0.5"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
@@ -5087,28 +4000,6 @@ node-forge@^1.3.1:
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
   integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
 
-node-int64@^0.4.0:
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
-  integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
-
-node-releases@^2.0.18:
-  version "2.0.18"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
-  integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
-
-normalize-path@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
-  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-
-npm-run-path@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
-  integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
-  dependencies:
-    path-key "^3.0.0"
-
 object-assign@^4, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5195,13 +4086,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
   dependencies:
     wrappy "1"
 
-onetime@^5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
-  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
-  dependencies:
-    mimic-fn "^2.1.0"
-
 optionator@^0.9.3:
   version "0.9.4"
   resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
@@ -5221,7 +4105,7 @@ p-limit@^2.0.0, p-limit@^2.2.0:
   dependencies:
     p-try "^2.0.0"
 
-p-limit@^3.0.1, p-limit@^3.0.2, p-limit@^3.1.0:
+p-limit@^3.0.1, p-limit@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
   integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@@ -5295,16 +4179,6 @@ parse-json@^4.0.0:
     error-ex "^1.3.1"
     json-parse-better-errors "^1.0.1"
 
-parse-json@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
-  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
-    error-ex "^1.3.1"
-    json-parse-even-better-errors "^2.3.0"
-    lines-and-columns "^1.1.6"
-
 parse-srcset@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
@@ -5350,7 +4224,7 @@ path-is-absolute@^1.0.0:
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
   integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
 
-path-key@^3.0.0, path-key@^3.1.0:
+path-key@^3.1.0:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
   integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@@ -5378,17 +4252,27 @@ path-type@^4.0.0:
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
+pathe@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
+  integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
+
+pathval@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
+  integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
+
 pend@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
   integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
 
-picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
+picocolors@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
   integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
 
-picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
+picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
   integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -5459,11 +4343,6 @@ pino@^9.5.0:
     sonic-boom "^4.0.1"
     thread-stream "^3.0.0"
 
-pirates@^4.0.4:
-  version "4.0.6"
-  resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
-  integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
-
 pkg-conf@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae"
@@ -5480,7 +4359,7 @@ pkg-conf@^4.0.0:
     find-up "^6.0.0"
     load-json-file "^7.0.0"
 
-pkg-dir@^4.1.0, pkg-dir@^4.2.0:
+pkg-dir@^4.1.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
   integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@@ -5497,7 +4376,7 @@ possible-typed-array-names@^1.0.0:
   resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
   integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
 
-postcss@^8.3.11:
+postcss@^8.3.11, postcss@^8.4.43:
   version "8.4.49"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
   integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
@@ -5529,15 +4408,6 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
 
-pretty-format@^29.0.0, pretty-format@^29.7.0:
-  version "29.7.0"
-  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
-  integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
-  dependencies:
-    "@jest/schemas" "^29.6.3"
-    ansi-styles "^5.0.0"
-    react-is "^18.0.0"
-
 process-warning@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
@@ -5548,14 +4418,6 @@ process-warning@^4.0.0:
   resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a"
   integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==
 
-prompts@^2.0.1:
-  version "2.4.2"
-  resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
-  integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
-  dependencies:
-    kleur "^3.0.3"
-    sisteransi "^1.0.5"
-
 prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@@ -5615,11 +4477,6 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
   integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
 
-pure-rand@^6.0.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2"
-  integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==
-
 qs@6.13.0:
   version "6.13.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
@@ -5691,11 +4548,6 @@ react-is@^16.13.1:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
-react-is@^18.0.0:
-  version "18.3.1"
-  resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
-  integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
-
 "readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
@@ -5743,34 +4595,12 @@ regexpp@^3.0.0:
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
   integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
 
-require-directory@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
-  integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
-
-resolve-cwd@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
-  integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
-  dependencies:
-    resolve-from "^5.0.0"
-
 resolve-from@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
   integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
 
-resolve-from@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
-  integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-
-resolve.exports@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
-  integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
-
-resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4:
+resolve@^1.22.1, resolve@^1.22.4:
   version "1.22.8"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
   integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -5828,6 +4658,34 @@ robust-predicates@^2.0.4:
   resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b"
   integrity sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==
 
+rollup@^4.20.0:
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.28.1.tgz#7718ba34d62b449dfc49adbfd2f312b4fe0df4de"
+  integrity sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==
+  dependencies:
+    "@types/estree" "1.0.6"
+  optionalDependencies:
+    "@rollup/rollup-android-arm-eabi" "4.28.1"
+    "@rollup/rollup-android-arm64" "4.28.1"
+    "@rollup/rollup-darwin-arm64" "4.28.1"
+    "@rollup/rollup-darwin-x64" "4.28.1"
+    "@rollup/rollup-freebsd-arm64" "4.28.1"
+    "@rollup/rollup-freebsd-x64" "4.28.1"
+    "@rollup/rollup-linux-arm-gnueabihf" "4.28.1"
+    "@rollup/rollup-linux-arm-musleabihf" "4.28.1"
+    "@rollup/rollup-linux-arm64-gnu" "4.28.1"
+    "@rollup/rollup-linux-arm64-musl" "4.28.1"
+    "@rollup/rollup-linux-loongarch64-gnu" "4.28.1"
+    "@rollup/rollup-linux-powerpc64le-gnu" "4.28.1"
+    "@rollup/rollup-linux-riscv64-gnu" "4.28.1"
+    "@rollup/rollup-linux-s390x-gnu" "4.28.1"
+    "@rollup/rollup-linux-x64-gnu" "4.28.1"
+    "@rollup/rollup-linux-x64-musl" "4.28.1"
+    "@rollup/rollup-win32-arm64-msvc" "4.28.1"
+    "@rollup/rollup-win32-ia32-msvc" "4.28.1"
+    "@rollup/rollup-win32-x64-msvc" "4.28.1"
+    fsevents "~2.3.2"
+
 run-parallel@^1.1.9:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -5886,12 +4744,12 @@ semver@^5.6.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
   integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
 
-semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
+semver@^6.0.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
 
-semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3:
+semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4, semver@^7.6.3:
   version "7.6.3"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
   integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
@@ -6008,10 +4866,10 @@ sift@16.0.1:
   resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053"
   integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==
 
-signal-exit@^3.0.3, signal-exit@^3.0.7:
-  version "3.0.7"
-  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
-  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+siginfo@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
+  integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
 
 signal-exit@^4.0.1:
   version "4.1.0"
@@ -6039,11 +4897,6 @@ simple-swizzle@^0.2.2:
   dependencies:
     is-arrayish "^0.3.1"
 
-sisteransi@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
-  integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
-
 slash@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -6089,19 +4942,6 @@ source-map-js@^1.2.1:
   resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
   integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
 
-source-map-support@0.5.13:
-  version "0.5.13"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
-  integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
-  dependencies:
-    buffer-from "^1.0.0"
-    source-map "^0.6.0"
-
-source-map@^0.6.0, source-map@^0.6.1:
-  version "0.6.1"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
-  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
 sparse-bitfield@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
@@ -6126,17 +4966,10 @@ sprintf-js@^1.1.3:
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
   integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
 
-sprintf-js@~1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
-  integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
-
-stack-utils@^2.0.3:
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
-  integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
-  dependencies:
-    escape-string-regexp "^2.0.0"
+stackback@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
+  integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
 
 standard-engine@^15.0.0:
   version "15.1.0"
@@ -6153,6 +4986,11 @@ statuses@2.0.1:
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
   integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
 
+std-env@^3.8.0:
+  version "3.8.0"
+  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
+  integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
+
 stream-events@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5"
@@ -6176,14 +5014,6 @@ streamx@^2.15.0, streamx@^2.20.0:
   optionalDependencies:
     bare-events "^2.2.0"
 
-string-length@^4.0.1:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
-  integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==
-  dependencies:
-    char-regex "^1.0.2"
-    strip-ansi "^6.0.0"
-
 "string-width-cjs@npm:string-width@^4.2.0":
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -6193,7 +5023,7 @@ string-length@^4.0.1:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.1"
 
-string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+string-width@^4.1.0:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -6298,16 +5128,6 @@ strip-bom@^3.0.0:
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
   integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
 
-strip-bom@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
-  integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
-
-strip-final-newline@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
-  integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-
 strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -6376,13 +5196,6 @@ supports-color@^7.1.0:
   dependencies:
     has-flag "^4.0.0"
 
-supports-color@^8.0.0:
-  version "8.1.1"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
-  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
-  dependencies:
-    has-flag "^4.0.0"
-
 supports-preserve-symlinks-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
@@ -6448,15 +5261,6 @@ teeny-request@^8.0.0:
     stream-events "^1.0.5"
     uuid "^9.0.0"
 
-test-exclude@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
-  integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
-  dependencies:
-    "@istanbuljs/schema" "^0.1.2"
-    glob "^7.1.4"
-    minimatch "^3.0.4"
-
 text-decoder@^1.1.0:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.1.tgz#e173f5121d97bfa3ff8723429ad5ba92e1ead67e"
@@ -6487,11 +5291,36 @@ tiny-case@^1.0.3:
   resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
   integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
 
+tinybench@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
+  integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
+
+tinyexec@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
+  integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
+
+tinypool@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2"
+  integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==
+
 tinyqueue@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
   integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
 
+tinyrainbow@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
+  integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
+
+tinyspy@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
+  integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
+
 title-case@^1.1.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/title-case/-/title-case-1.1.2.tgz#fae4a6ae546bfa22d083a0eea910a40d12ed4f5a"
@@ -6500,11 +5329,6 @@ title-case@^1.1.0:
     sentence-case "^1.1.1"
     upper-case "^1.0.3"
 
-tmpl@1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
-  integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
-
 to-regex-range@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -6541,21 +5365,6 @@ tr46@~0.0.3:
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
   integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
 
-ts-jest@^29.2.5:
-  version "29.2.5"
-  resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63"
-  integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==
-  dependencies:
-    bs-logger "^0.2.6"
-    ejs "^3.1.10"
-    fast-json-stable-stringify "^2.1.0"
-    jest-util "^29.0.0"
-    json5 "^2.2.3"
-    lodash.memoize "^4.1.2"
-    make-error "^1.3.6"
-    semver "^7.6.3"
-    yargs-parser "^21.1.1"
-
 ts-standard@^12.0.0:
   version "12.0.2"
   resolved "https://registry.yarnpkg.com/ts-standard/-/ts-standard-12.0.2.tgz#883db655106f9bde374348fc81c89e8a30414e1b"
@@ -6620,21 +5429,11 @@ type-check@^0.4.0, type-check@~0.4.0:
   dependencies:
     prelude-ls "^1.2.1"
 
-type-detect@4.0.8:
-  version "4.0.8"
-  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
-  integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
-
 type-fest@^0.20.2:
   version "0.20.2"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
   integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
 
-type-fest@^0.21.3:
-  version "0.21.3"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
-  integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
-
 type-fest@^0.3.0:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
@@ -6741,14 +5540,6 @@ unpipe@1.0.0, unpipe@~1.0.0:
   resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
   integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
 
-update-browserslist-db@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
-  integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==
-  dependencies:
-    escalade "^3.2.0"
-    picocolors "^1.1.0"
-
 upper-case-first@^1.1.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
@@ -6800,15 +5591,6 @@ uuid@^9.0.0:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
   integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
 
-v8-to-istanbul@^9.0.1:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175"
-  integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==
-  dependencies:
-    "@jridgewell/trace-mapping" "^0.3.12"
-    "@types/istanbul-lib-coverage" "^2.0.1"
-    convert-source-map "^2.0.0"
-
 value-or-promise@1.0.11:
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140"
@@ -6824,17 +5606,53 @@ vary@^1, vary@~1.1.2:
   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
   integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
 
-wait-for-expect@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463"
-  integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==
-
-walker@^1.0.8:
-  version "1.0.8"
-  resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
-  integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
+vite-node@2.1.8:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5"
+  integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==
   dependencies:
-    makeerror "1.0.12"
+    cac "^6.7.14"
+    debug "^4.3.7"
+    es-module-lexer "^1.5.4"
+    pathe "^1.1.2"
+    vite "^5.0.0"
+
+vite@^5.0.0:
+  version "5.4.11"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
+  integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==
+  dependencies:
+    esbuild "^0.21.3"
+    postcss "^8.4.43"
+    rollup "^4.20.0"
+  optionalDependencies:
+    fsevents "~2.3.3"
+
+vitest@^2.1.8:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa"
+  integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==
+  dependencies:
+    "@vitest/expect" "2.1.8"
+    "@vitest/mocker" "2.1.8"
+    "@vitest/pretty-format" "^2.1.8"
+    "@vitest/runner" "2.1.8"
+    "@vitest/snapshot" "2.1.8"
+    "@vitest/spy" "2.1.8"
+    "@vitest/utils" "2.1.8"
+    chai "^5.1.2"
+    debug "^4.3.7"
+    expect-type "^1.1.0"
+    magic-string "^0.30.12"
+    pathe "^1.1.2"
+    std-env "^3.8.0"
+    tinybench "^2.9.0"
+    tinyexec "^0.3.1"
+    tinypool "^1.0.1"
+    tinyrainbow "^1.2.0"
+    vite "^5.0.0"
+    vite-node "2.1.8"
+    why-is-node-running "^2.3.0"
 
 webidl-conversions@^3.0.0:
   version "3.0.1"
@@ -6933,6 +5751,14 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+why-is-node-running@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
+  integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
+  dependencies:
+    siginfo "^2.0.0"
+    stackback "0.0.2"
+
 word-wrap@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
@@ -6947,15 +5773,6 @@ word-wrap@^1.2.5:
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
 
-wrap-ansi@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
-  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
-  dependencies:
-    ansi-styles "^4.0.0"
-    string-width "^4.1.0"
-    strip-ansi "^6.0.0"
-
 wrap-ansi@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
@@ -6970,52 +5787,16 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
-write-file-atomic@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
-  integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
-  dependencies:
-    imurmurhash "^0.1.4"
-    signal-exit "^3.0.7"
-
 xdg-basedir@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
   integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
 
-y18n@^5.0.5:
-  version "5.0.8"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
-  integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-
-yallist@^3.0.2:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
-  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
-
 yallist@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yargs-parser@^21.1.1:
-  version "21.1.1"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
-  integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
-
-yargs@^17.3.1:
-  version "17.7.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
-  integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
-  dependencies:
-    cliui "^8.0.1"
-    escalade "^3.1.1"
-    get-caller-file "^2.0.5"
-    require-directory "^2.1.1"
-    string-width "^4.2.3"
-    y18n "^5.0.5"
-    yargs-parser "^21.1.1"
-
 yauzl@^3.1.3:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-3.2.0.tgz#7b6cb548f09a48a6177ea0be8ece48deb7da45c0"