From d7b432a8b81b96f77b45525e1afb5d52dd7fe783 Mon Sep 17 00:00:00 2001 From: CocoisBuggy Date: Sat, 14 Dec 2024 09:00:47 +0200 Subject: [PATCH] Passing most tests stable with vitest fixtures The test speed is up roughly 10x but my expectation is that it should be able to come lower than that. The beauty of the way test containment is implemented here means that there is not much need for isolation - which is good for simulating our use case because we make use of a lot of singletons (though thankfully not TOO much shared state) It was all relatively trivial with the exception of histories. I am intensely anxious with the implementation that has been used for document history, as it obscures control flow and does not look like it will scale - especially if we were ever to want horizontal deployment. All in all the tests are faster, and can tighten up development feedback loops BUT the un-strict nature of our existing test runners means that there are some inconsistencies in execution that I haven't yet uncovered --- .gitignore | 3 + .vscode/launch.json | 12 + package.json | 5 +- src/__tests__/areas.ts | 121 +- src/__tests__/bulkImport.test.ts | 97 +- src/__tests__/fixtures/data.fixtures.ts | 186 ++ src/__tests__/fixtures/gql.fixtures.ts | 104 +- src/__tests__/fixtures/mongo.fixtures.ts | 64 +- src/__tests__/history.ts | 89 +- src/__tests__/organizations.ts | 225 +- src/__tests__/ticks.ts | 137 +- src/model/MutableAreaDataSource.ts | 1 + src/model/__tests__/AreaHistoryDataSource.ts | 186 +- src/model/__tests__/AreaUtils.ts | 2 +- src/model/__tests__/BulkDataSource.test.ts | 147 +- src/model/__tests__/ChangeLogDS.ts | 28 +- src/model/__tests__/MediaDataSource.ts | 178 +- .../__tests__/MutableAreaDataSource.test.ts | 112 +- src/model/__tests__/MutableClimbDataSource.ts | 178 +- .../MutableOrganizationDataSource.ts | 160 +- src/model/__tests__/UserDataSource.ts | 35 +- src/model/__tests__/ticks.ts | 201 +- src/model/__tests__/updateAreas.ts | 235 +- src/server.ts | 6 +- src/utils/inMemoryDB.ts | 84 - src/utils/testUtils.ts | 75 - vite.config.ts | 9 +- yarn.lock | 2237 ++++------------- 28 files changed, 1641 insertions(+), 3276 deletions(-) create mode 100644 src/__tests__/fixtures/data.fixtures.ts delete mode 100644 src/utils/inMemoryDB.ts diff --git a/.gitignore b/.gitignore index e54f7667..be0585d7 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 42b1b203..fa9c1e18 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -76,5 +76,17 @@ ], "console": "integratedTerminal" }, + { + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "type": "node", + "request": "launch", + "name": "Debug Tests", + "autoAttachChildProcesses": true, + "skipFiles": ["/**", "**/node_modules/**"], + "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", + "args": ["run"], + "smartStep": true, + "console": "integratedTerminal" + } ] } \ No newline at end of file diff --git a/package.json b/package.json index a95c9f00..4cca9b9f 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,7 @@ "supertest": "^6.3.3", "ts-standard": "^12.0.0", "typescript": "4.9.5", - "vitest": "^2.1.8", - "wait-for-expect": "^3.0.2" + "vitest": "^2.1.8" }, "dependencies": { "@apollo/server": "^4.11.2", @@ -70,7 +69,7 @@ "scripts": { "lint": "yarn ts-standard", "fix": "yarn ts-standard --fix", - "test": "vitest run --no-file-parallelism", + "test": "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 8ac46bd5..07446881 100644 --- a/src/__tests__/areas.ts +++ b/src/__tests__/areas.ts @@ -1,50 +1,35 @@ -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 { 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' +interface LocalContext { + includedChild: AreaType + excludedArea: AreaType + alphaFields: OrganizationEditableFieldsType + alphaOrg: OrganizationType +} -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({ + 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) + } +}) +describe('areas API', () => { describe('queries', () => { const areaQuery = ` query area($input: ID) { @@ -56,50 +41,50 @@ 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)) + console.log(areaResult) expect(areaResult.organizations).toHaveLength(1) expect(areaResult.organizations[0].orgId).toBe(muuidToString(alphaOrg.orgId)) }) 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({ + 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..5425500d --- /dev/null +++ b/src/__tests__/fixtures/data.fixtures.ts @@ -0,0 +1,186 @@ +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' + +interface DbTestContext { + user: MUUID + userUuid: string + profile: UserPublicProfile + + addArea: (name?: string, extra?: Partial<{ leaf: boolean, boulder: boolean, parent: MUUID | AreaType }>) => Promise + countryCode: Alpha3Code + country: AreaType + area: AreaType + + addClimb: (props?: Partial) => Promise + 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({ + 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 { + 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 ({ task, addArea }, use) => { + await use(await addArea()) + }, + + addClimb: async ({ climbs, area, task, user }, use) => { + async function addClimb (data?: Partial): Promise { + const [id] = await climbs.addOrUpdateClimbs(user, area.metadata.area_id, [{ + name: task.id + process.uptime().toString(), + 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 system: GradeScalesTypes = gradeContextToGradeScales[country.gradeContext]?.[Object.keys(climb.type).filter(type => climb.type[type])[0]] + 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) + + console.log({ grade, type: climb.type, scale }) + 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 index 3a90fd05..f9d0fcbf 100644 --- a/src/__tests__/fixtures/gql.fixtures.ts +++ b/src/__tests__/fixtures/gql.fixtures.ts @@ -1,18 +1,17 @@ import { ApolloServer, BaseContext } from '@apollo/server' import express, { Application } from 'express' -import { dbTest } from './mongo.fixtures' -import { QueryAPIProps } from '../../utils/testUtils' import request from 'supertest' import jwt from 'jsonwebtoken' -import { expressMiddleware } from '@apollo/server/dist/esm/express4' +import { expressMiddleware } from '@apollo/server/express4' import bodyParser from 'body-parser' import { applyMiddleware } from 'graphql-middleware' -import { localDevBypassAuthContext } from '../../auth/local-dev/middleware' -import localDevBypassAuthPermissions from '../../auth/local-dev/permissions' import { graphqlSchema } from '../../graphql/resolvers' import cors from 'cors' -import { muuidToString } from '../../utils/helpers' -import muuid, { MUUID } from 'uuid-mongodb' +import { dataFixtures } from './data.fixtures' +import { createContext, permissions } from '../../auth' + +let server: ApolloServer +let app: Application interface ServerTestFixtures { ctx: { @@ -20,60 +19,72 @@ interface ServerTestFixtures { app: Application } query: (opts: QueryAPIProps) => Promise - user: MUUID - userUuid: string } -export const serverTest = dbTest.extend({ +export interface QueryAPIProps { + query?: string + operationName?: string + variables?: any + userUuid?: string + roles?: string[] + port?: number + endpoint?: string + app?: express.Application + body?: any +} + +export const gqlTest = dataFixtures.extend({ ctx: async ({ - task, climbs, areas, bulkImport, + climbs, areas, bulkImport, organizations, ticks, history, media, users }, use) => { - const schema = applyMiddleware( - graphqlSchema, - (localDevBypassAuthPermissions).generate(graphqlSchema) - ) - const dataSources = ({ - climbs, - areas, - bulkImport, - organizations, - ticks, - history, - media, - users - }) - - const app = express() + if (app === undefined) { + app = express() + } - const server = new ApolloServer({ - schema, - introspection: false, - plugins: [] - }) - // server must be started before applying middleware - await server.start() + if (server === undefined) { + const schema = applyMiddleware( + graphqlSchema, + permissions.generate(graphqlSchema) + ) - const context = localDevBypassAuthContext + const dataSources = ({ + climbs, + areas, + bulkImport, + organizations, + ticks, + history, + media, + users + }) - app.use('/', - bodyParser.json({ limit: '10mb' }), - cors(), - express.json(), - expressMiddleware(server, { - context: async ({ req }) => ({ dataSources, ...await context({ req }) }) + server = new ApolloServer({ + schema, + introspection: false, + plugins: [] }) - ) + + // server must be started before applying middleware + await server.start() + + app.use('/', + bodyParser.json({ limit: '10mb' }), + cors(), + express.json(), + expressMiddleware(server, { + context: async ({ req }) => ({ dataSources, ...await createContext({ req }) }) + }) + ) + } await use({ app, server }) - - await server.stop() }, query: async ({ ctx }, use) => { @@ -108,8 +119,5 @@ export const serverTest = dbTest.extend({ return await req } ) - }, - - user: async ({ task }, use) => await use(muuid.mode('relaxed').from(task.id)), - userUuid: async ({ user }, use) => await use(muuidToString(user)) + } }) diff --git a/src/__tests__/fixtures/mongo.fixtures.ts b/src/__tests__/fixtures/mongo.fixtures.ts index 87bae34f..de7dd8e3 100644 --- a/src/__tests__/fixtures/mongo.fixtures.ts +++ b/src/__tests__/fixtures/mongo.fixtures.ts @@ -1,9 +1,10 @@ +/* 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 { testStreamListener } from '../../db/edit/streamListener' -import { Mock } from 'vitest' import MutableAreaDataSource from '../../model/MutableAreaDataSource' import MutableClimbDataSource from '../../model/MutableClimbDataSource' import BulkImportDataSource from '../../model/BulkImportDataSource' @@ -12,6 +13,8 @@ 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' /** * In-memory Mongo replset used for testing. @@ -19,9 +22,8 @@ import UserDataSource from '../../model/UserDataSource' * Need a replset to faciliate transactions. */ let mongod: MongoMemoryReplSet -const onChange: Mock = vi.fn() -let stream: ChangeStream let uri: string +let stream: ChangeStream beforeAll(async () => { mongod = await MongoMemoryReplSet.create({ @@ -32,15 +34,7 @@ beforeAll(async () => { 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(async () => await testStreamListener(onChange)) -}) - -afterAll(async () => { - await stream?.close() - await mongoose.connection.dropDatabase() - await mongoose.connection.close() - await mongod.stop() + stream = await defaultPostConnect() }) interface DbTestContext { @@ -56,6 +50,9 @@ interface DbTestContext { history: ChangeLogDataSource media: MutableMediaDataSource users: UserDataSource + changeLog: ChangeLogDataSource + + waitForChanges: (props: WaitProps) => Promise } export const dbTest = test.extend({ @@ -97,5 +94,44 @@ export const dbTest = test.extend({ 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()) + 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() + + async function wait (props: WaitProps): Promise { + return await new Promise((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(console.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__/history.ts b/src/__tests__/history.ts index 0590fd0c..7dbd9196 100644 --- a/src/__tests__/history.ts +++ b/src/__tests__/history.ts @@ -1,47 +1,10 @@ -import { ApolloServer } from '@apollo/server' -import muuid from 'uuid-mongodb' -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' +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 { @@ -86,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) /** @@ -132,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({ + 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 32c5df61..31d0bc5a 100644 --- a/src/__tests__/ticks.ts +++ b/src/__tests__/ticks.ts @@ -1,32 +1,16 @@ -import { ApolloServer } from '@apollo/server' -import muuid from 'uuid-mongodb' -import { queryAPI, setUpServer } from '../utils/testUtils.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 { TickInput, TickType } from '../db/TickTypes.js' import { UpdateProfileGQLInput } from '../db/UserTypes.js' -import { InMemoryDB } from '../utils/inMemoryDB.js' -import express from 'express' - -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 +import { muuidToString } from '../utils/helpers.js' +import { gqlTest } from './fixtures/gql.fixtures.js' - beforeAll(async () => { - ({ server, inMemoryDB, app } = await setUpServer()) - user = muuid.v4() - userUuid = muuidToString(user) +interface LocalContext { + singleTickData: TickInput + tick: TickType +} - tickOne = { +const it = gqlTest.extend({ + singleTickData: async ({ userUuid }, use) => { + await use({ name: 'Route One', notes: 'Nice slab', climbId: 'c76d2083-6b8f-524a-8fb8-76e1dc79833f', @@ -36,20 +20,14 @@ describe('ticks API', () => { dateClimbed: new Date('2016-07-20T17:30:15+05:30'), grade: '5.8', source: 'MP' - } - }) - - beforeEach(async () => { - ticks = TickDataSource.getInstance() - users = UserDataSource.getInstance() - await inMemoryDB.clear() - }) - - 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 = ` query userTicks($userId: MUUID, $username: String) { @@ -82,58 +60,41 @@ 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) }, + userUuid }) + 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) 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) }) }) @@ -170,29 +131,28 @@ 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) 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: { @@ -208,8 +168,7 @@ describe('ticks API', () => { } }, userUuid, - roles: [], // ['user_admin'], - app + roles: [] }) expect(updateResponse.statusCode).toBe(200) diff --git a/src/model/MutableAreaDataSource.ts b/src/model/MutableAreaDataSource.ts index 4eba2c64..f5377e22 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 `Missing lnglat for ${countryName}` } await this.validateUniqueAreaName(countryName, null) diff --git a/src/model/__tests__/AreaHistoryDataSource.ts b/src/model/__tests__/AreaHistoryDataSource.ts index 6795efb0..f289f44a 100644 --- a/src/model/__tests__/AreaHistoryDataSource.ts +++ b/src/model/__tests__/AreaHistoryDataSource.ts @@ -1,159 +1,131 @@ 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 { Mock } from 'vitest' +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: Mock - const testUser = muuid.v4() + it('should create history changes for an area when children get added to it', async ({ changeLog, area, addArea, country, waitForChanges }) => { + const historySettled = waitForChanges({ document: area, count: 2 }) + await addArea('nevada', { parent: area }) + await addArea('oregon', { parent: area }) + await historySettled - beforeAll(async () => { - onChange = vi.fn() - await inMemoryDB.connect(onChange) - await ChangeLogDataSource.getInstance()._testRemoveAll() - - areas = MutableAreaDataSource.getInstance() + expect(await changeLog.getAreaChangeSets(area.metadata.area_id)).toHaveLength(2) }) - afterAll(async () => { - try { - await inMemoryDB.close() - } catch (e) { - console.log('closing mongoose', e) - } - }) + 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 - beforeEach(async () => { - await ChangeLogDataSource.getInstance()._testRemoveAll() - onChange.mockClear() + 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) + 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)) + }) - // Verify NV history - const nvAreaHistory = areaHistory[0].changes - expect(nvAreaHistory).toHaveLength(2) + 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 }) - // 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? + await mainAreaHistory + + const initialHistory = await changeLog.getAreaChangeSets(area.metadata.area_id) + const nvAreaHistory: Array> = 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?.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) + 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 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') + // 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) + 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 - expect(squamish?._id).toBeTruthy() + await areas.setDestinationFlag(user, areaUuid, true) + await areas.setDestinationFlag(user, areaUuid, false) - 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 new Promise((resolve) => setTimeout(resolve, 200)) + const changset = await changeLog.getAreaChangeSets(areaUuid) - await areas.setDestinationFlag(testUser, areaUuid, true) - await areas.setDestinationFlag(testUser, areaUuid, false) + expect(changset).toHaveLength(3) + expect(changset[0].operation).toEqual('updateDestination') + expect(changset[1].operation).toEqual('updateDestination') + expect(changset[2].operation).toEqual('addArea') - 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 - } + 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) - assert(leonidio != null) + 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 }) - 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) - - assert(margalef != null) + 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 + await addArea(undefined, { parent: child }) - const newChild = await areas.addArea(testUser, 'One', margalef.metadata.area_id) + await expect(async () => await areas.deleteArea(user, child.metadata.area_id)).rejects.toThrow() + await process - assert(newChild != null) - - await expect(async () => await areas.deleteArea(testUser, margalef.metadata.area_id)).rejects.toThrow() - - await waitForExpect(() => expect(onChange).toHaveBeenCalledTimes(5)) - const history = await ChangeLogDataSource.getInstance().getAreaChangeSets(spain.metadata.area_id) + 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 +} + +const it = dataFixtures.extend({ + assertBulkImport: async ({ climbs, user, bulkImport }, use) => { + const assertBulkImport = async (...input: BulkImportAreaInputType[]): Promise => { + 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 => { - 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[], }); }); - 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 64bf0cca..ff578a31 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, @@ -14,116 +9,75 @@ import { UserMedia, UserMediaQueryInput } 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 climbIdForTagging: MUUID - - 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() - - 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') - - assert(areaForTagging1 != null, 'Fail to pre-seed test areas') - assert(areaForTagging2 != null, 'Fail to pre-seed test areas') - - const rs = await climbs.addOrUpdateClimbs(muuid.v4(), areaForTagging1.metadata.area_id, [newSportClimb1]) - assert(rs != null, 'Fail to pre-seed test areas') - climbIdForTagging = muuid.from(rs[0]) +import { gqlTest } from '../../__tests__/fixtures/gql.fixtures.js' - const rs2 = await media.addMediaObjects([TEST_MEDIA]) - testMediaObject = rs2[0] +interface LocalContext { + mediaInput: MediaObjectGQLInput + mediaObject: MediaObject + otherArea: AreaType - assert(testMediaObject != null, '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({ + 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 () => { - assert(areaForTagging1 != null, '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 @@ -132,14 +86,14 @@ describe('MediaDataSource', () => { expect(tag).toMatchObject>({ 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 @@ -147,14 +101,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) @@ -166,16 +120,16 @@ 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]) @@ -183,9 +137,9 @@ describe('MediaDataSource', () => { 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' }]) @@ -197,21 +151,21 @@ describe('MediaDataSource', () => { 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(async () => await media.deleteMediaObject(rs[0]._id)).rejects.toThrowError('Cannot delete media object with non-empty tags.') }) - it('should return paginated media results', async () => { + it('should return paginated media results', async ({ mediaInput, media }) => { const ITEMS_PER_PAGE = 3 const MEDIA_TEMPLATE: MediaObjectGQLInput = { - ...TEST_MEDIA, + ...mediaInput, userUuid: 'a0ca9ebb-aa3b-4bb0-8ddd-7c8b2ed228a5' } 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) })) 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 & { metadata: Partial}>) })) - 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 & { metadata: Partial}>) })) - 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) })) - 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 c8f39b98..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') + 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') + 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]) @@ -227,12 +185,10 @@ describe('Climb CRUD', () => { expect(newClimb.name).toBe(newBoulderProblem1.name) }) - it('can delete new boulder problems', async () => { - const newBoulderingArea = await areas.addArea(testUser, 'Bouldering area 1', null, 'fr') - expect(newBoulderingArea).toBeTruthy() - + 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()]) @@ -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') + 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,12 +254,12 @@ 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') + const newClimbingArea = await areas.addArea(user, 'Climbing area 1', null, 'aus') expect(newClimbingArea).toBeTruthy() const newclimbs = [ @@ -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') + 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,12 +329,12 @@ 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') + const newClimbingArea = await areas.addArea(user, 'Climbing area in Brazil', null, 'bra') expect(newClimbingArea).toBeTruthy() const newclimbs = [ @@ -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') + 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') + 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') - - expect(newDestination).toBeTruthy() - + 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') - - expect(newDestination).toBeTruthy() + 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') + 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] ) @@ -612,13 +562,13 @@ describe('Climb CRUD', () => { }) }) - 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') expect(newDestination).toBeTruthy() const newIDs = await climbs.addOrUpdateClimbs( - testUser, + user, newDestination.metadata.area_id, [newClimbWithPitches] ) @@ -667,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])) @@ -696,11 +646,11 @@ describe('Climb CRUD', () => { } // Check that the createdBy and updatedBy fields are not undefined before accessing their properties - assert(updatedClimb.createdBy != undefined) - assert(updatedClimb.updatedBy != undefined) + assert(updatedClimb.createdBy !== undefined) + assert(updatedClimb.updatedBy !== undefined) - expect(updatedClimb.createdBy.toUUID().toString()).toEqual(testUser.toString()) - expect(updatedClimb.updatedBy.toUUID().toString()).toEqual(testUser.toString()) + 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 766ede95..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({ + 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) + }, + + emptyOrg: async ({ task }, use) => { + await use({ + displayName: `Foes of ${task.id}` + }) + } +}) - it('should successfully create a document when passed valid input', async () => { - const newOrg = await organizations.addOrganization(testUser, OrgType.localClimbingOrganization, fullOrg) - const document = { ...fullOrg } +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,37 +63,51 @@ 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() assert(updatedOrg != null) @@ -131,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 584ca71a..b5e5e722 100644 --- a/src/model/__tests__/UserDataSource.ts +++ b/src/model/__tests__/UserDataSource.ts @@ -1,35 +1,14 @@ -import mongoose from 'mongoose' import muuid from 'uuid-mongodb' - -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(() => { 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 = { @@ -52,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' @@ -99,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 = { @@ -112,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 = { @@ -131,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 = { @@ -158,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__/ticks.ts b/src/model/__tests__/ticks.ts index 0f52897b..d79f7598 100644 --- a/src/model/__tests__/ticks.ts +++ b/src/model/__tests__/ticks.ts @@ -1,177 +1,130 @@ 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 { 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' - -const userId = muuid.v4() - -const toTest: TickInput = { - name: 'Small Dog', - notes: 'Sandbagged', - climbId: 'c76d2083-6b8f-524a-8fb8-76e1dc79833f', - userId: userId.toUUID().toString(), - style: 'Lead', - attemptType: 'Onsight', - dateClimbed: new Date('2012-12-12'), - grade: '5.7', - source: 'MP' -} -const toTest2: TickInput = { - name: 'Sloppy Peaches', - notes: 'v sloppy', - climbId: 'b767d949-0daf-5af3-b1f1-626de8c84b2a', - userId: userId.toUUID().toString(), - style: 'Lead', - attemptType: 'Flash', - dateClimbed: new Date('2012-10-15'), - grade: '5.10', - source: 'MP' +interface LocalContext { + tickImportData: TickInput[] + tickData: TickInput + tickUpdateData: TickInput + tick: TickType } -const tickUpdate: TickInput = produce(toTest, draft => { - draft.notes = 'Not sandbagged' - draft.attemptType = 'Fell/Hung' - draft.source = 'OB' +const it = dataFixtures.extend({ + 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: 'Onsight', + dateClimbed: new Date(), + grade: '5.7', + source: 'MP' + } + ))), + tickData: async ({ task, userUuid }, use) => await use({ + name: 'Small Dog', + notes: 'Sandbagged', + climbId: 'c76d2083-6b8f-524a-8fb8-76e1dc79833f', + userId: userUuid, + style: 'Lead', + attemptType: 'Onsight', + 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 = 'Fell/Hung' + 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 - 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() - users = UserDataSource.getInstance() - }) - - 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) - - expect(tick).not.toBeNull() - - const newTick = await ticks.editTick({ _id: tick._id }, tickUpdate) + it('should update a tick and return the proper information', async ({ ticks, tick, tickUpdateData }) => { + const newTick = await ticks.editTick({ _id: tick._id }, tickUpdateData) expect(newTick).not.toBeNull() 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) - - expect(tick).not.toBeNull() + 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) expect(newTicks).not.toBeNull() - expect(newTicks).toHaveLength(testImport.length) + 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) - - expect(tick).not.toBeNull() - - 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 = 'c76d2083-6b8f-524a-8fb8-76e1dc79833f' - const tick = await ticks.addTick(toTest) - const tick2 = await ticks.addTick(toTest2) - - expect(tick).not.toBeNull() - expect(tick2).not.toBeNull() + 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()) }) + ]) - 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) expect(newTicks).not.toBeNull() - expect(newTicks).toHaveLength(3) + 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) + 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 131bfe5f..c9b9ba15 100644 --- a/src/model/__tests__/updateAreas.ts +++ b/src/model/__tests__/updateAreas.ts @@ -1,129 +1,103 @@ 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 { GradeContexts, 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() - }) - - afterAll(async () => { - await inMemoryDB.close() + 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) }) - 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 ({ 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-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) - assert(bc != null) - assert(canada != null) + const district = await areas.addArea(user, 'British Columbia', country.metadata.area_id) + assert(district != null) + assert(country != null) - 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]) + if (province != null) { // make TS happy + 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') - - assert(a1 != null) - + it('should update multiple fields', async ({ areas, user, area }) => { // for testing area desccription is sanitized const iframeStr = '' const doc1: AreaEditableFieldsType = { @@ -132,7 +106,7 @@ describe('Areas', () => { description: `This is a cool area 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) @@ -145,37 +119,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') - assert(country != null) - - 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) 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) @@ -186,9 +151,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) @@ -200,60 +165,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) - assert(kali != null) + 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) 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(), @@ -265,7 +230,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) @@ -287,15 +252,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( @@ -344,7 +309,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/server.ts b/src/server.ts index ea3e66b8..6eab9995 100644 --- a/src/server.ts +++ b/src/server.ts @@ -25,7 +25,7 @@ import BulkImportDataSource from './model/BulkImportDataSource.js' /** * Create a GraphQL server */ -export async function createServer (serve = true): Promise<{ app: express.Application, server: ApolloServer }> { +export async function createServer (): Promise<{ app: express.Application, server: ApolloServer }> { const schema = applyMiddleware( graphqlSchema, (process.env.LOCAL_DEV_BYPASS_AUTH === 'true' ? localDevBypassAuthPermissions : permissions).generate(graphqlSchema) @@ -66,8 +66,6 @@ export async function createServer (serve = true): Promise<{ app: express.Applic }) ) - if (serve) { - await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve)) - } + await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve)) return { app, server } } 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 => { - 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 => { - 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 => { - 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 => { - 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 - close: () => Promise - clear: () => Promise - insertDirectly: (collection: string, documents: any[]) => Promise -} - -export default { connect, close, clear, insertDirectly, stream } diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts index 5efc5632..e49da079 100644 --- a/src/utils/testUtils.ts +++ b/src/utils/testUtils.ts @@ -1,78 +1,3 @@ -import jwt from 'jsonwebtoken' -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 => { - // 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 - } - }) - - 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 => { - await inMemoryDB.connect() - - const { app, server } = await createServer(false) - return { app, server, inMemoryDB } -} - export const isFulfilled = ( p: PromiseSettledResult ): p is PromiseFulfilledResult => p.status === 'fulfilled' diff --git a/vite.config.ts b/vite.config.ts index 986aee6d..89255528 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,6 +5,13 @@ export default defineConfig({ 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"