diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 0a60723d..026d655b 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -10,6 +10,24 @@ on: - main types: [opened, synchronize, reopened, labeled] jobs: + cli: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Build and test ADC CLI + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@v2 + with: + version: latest + - name: Install dependencies + run: pnpm install + + # Run E2E tests + - name: Run E2E tests + run: npx nx run cli:e2e apisix: runs-on: ubuntu-latest strategy: diff --git a/apps/cli-e2e/.eslintrc.json b/apps/cli-e2e/.eslintrc.json deleted file mode 100644 index 9d9c0db5..00000000 --- a/apps/cli-e2e/.eslintrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - } - ] -} diff --git a/apps/cli-e2e/README.md b/apps/cli-e2e/README.md deleted file mode 100644 index b66ae09c..00000000 --- a/apps/cli-e2e/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# E2E Test for CLI - -**Currently we don't do E2E testing on the cli, but rather unit testing and E2E testing in each module separately.** - -**So this package is used as a placeholder in the project.** diff --git a/apps/cli-e2e/project.json b/apps/cli-e2e/project.json deleted file mode 100644 index f99b2058..00000000 --- a/apps/cli-e2e/project.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "cli-e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "implicitDependencies": [ - "cli" - ], - "projectType": "application", - "targets": { - "e2e": { - "executor": "@nx/jest:jest", - "outputs": [ - "{workspaceRoot}/coverage/{e2eProjectRoot}" - ], - "options": { - "jestConfig": "apps/cli-e2e/jest.config.ts", - "passWithNoTests": true - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": [ - "{options.outputFile}" - ] - } - }, -} \ No newline at end of file diff --git a/apps/cli-e2e/src/support/global-setup.ts b/apps/cli-e2e/src/support/global-setup.ts deleted file mode 100644 index bab0a073..00000000 --- a/apps/cli-e2e/src/support/global-setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default () => { - /* */ -}; diff --git a/apps/cli-e2e/tsconfig.json b/apps/cli-e2e/tsconfig.json deleted file mode 100644 index ed633e1d..00000000 --- a/apps/cli-e2e/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.spec.json" - } - ], - "compilerOptions": { - "esModuleInterop": true - } -} diff --git a/apps/cli-e2e/tsconfig.spec.json b/apps/cli-e2e/tsconfig.spec.json deleted file mode 100644 index d7f9cf20..00000000 --- a/apps/cli-e2e/tsconfig.spec.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["jest.config.ts", "src/**/*.ts"] -} diff --git a/apps/cli/e2e/server/basic.e2e-spec.ts b/apps/cli/e2e/server/basic.e2e-spec.ts new file mode 100644 index 00000000..d8ac375b --- /dev/null +++ b/apps/cli/e2e/server/basic.e2e-spec.ts @@ -0,0 +1,80 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import { lastValueFrom } from 'rxjs'; +import request from 'supertest'; + +import { ADCServer } from '../../src/server'; +import { jestMockBackend } from '../support/utils'; + +describe('Server - Basic', () => { + let mockedBackend: ADCSDK.Backend; + let server: ADCServer; + + beforeAll(async () => { + mockedBackend = jestMockBackend(); + server = new ADCServer(); + }); + + it('test mocked load backend', async () => { + const { status, body } = await request(server.TEST_ONLY_getExpress()) + .put('/sync') + .send({ + task: { + opts: { + backend: 'mock', + server: 'http://1.1.1.1:3000', + token: 'mock', + }, + config: {}, + }, + }); + + expect(status).toBe(202); + expect(body.status).toBe('success'); + await expect(lastValueFrom(mockedBackend.dump())).resolves.toEqual({}); + }); + + it('test real apisix backend (expect connect refused)', async () => { + const { status, body } = await request(server.TEST_ONLY_getExpress()) + .put('/sync') + .send({ + task: { + opts: { + backend: 'apisix', + server: 'http://127.0.0.1:50000', + token: 'mock', + }, + config: {}, + }, + }); + + expect(status).toBe(500); + expect(body.message).toBe('Error: connect ECONNREFUSED 127.0.0.1:50000'); + }); + + it('test sync', async () => { + const config = { + consumers: [ + { + username: 'test-consumer', + plugins: { 'limit-count': { count: 10, time_window: 60 } }, + }, + ], + } as ADCSDK.Configuration; + const { status, body } = await request(server.TEST_ONLY_getExpress()) + .put('/sync') + .send({ + task: { + opts: { + backend: 'mock', + server: 'http://1.1.1.1:3000', + token: 'mock', + }, + config: config, + }, + }); + + expect(status).toBe(202); + expect(body.status).toBe('success'); + await expect(lastValueFrom(mockedBackend.dump())).resolves.toEqual(config); + }); +}); diff --git a/apps/cli/e2e/support/global-setup.ts b/apps/cli/e2e/support/global-setup.ts new file mode 100644 index 00000000..db363033 --- /dev/null +++ b/apps/cli/e2e/support/global-setup.ts @@ -0,0 +1,5 @@ +import { configurePluralize } from '../../src/command/utils'; + +export default () => { + configurePluralize(); +}; diff --git a/apps/cli-e2e/src/support/global-teardown.ts b/apps/cli/e2e/support/global-teardown.ts similarity index 100% rename from apps/cli-e2e/src/support/global-teardown.ts rename to apps/cli/e2e/support/global-teardown.ts diff --git a/apps/cli/e2e/support/utils.ts b/apps/cli/e2e/support/utils.ts new file mode 100644 index 00000000..7a66c8a0 --- /dev/null +++ b/apps/cli/e2e/support/utils.ts @@ -0,0 +1,67 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import pluralize from 'pluralize'; +import { Subscription, from, of, switchMap, tap, toArray } from 'rxjs'; +import { SemVer } from 'semver'; + +import * as commandUtils from '../../src/command/utils'; + +export const mockBackend = (): ADCSDK.Backend => { + class MockBackend implements ADCSDK.Backend { + private readonly cache: { config?: ADCSDK.Configuration } = {}; + + public metadata() { + return { logScope: ['mock'] }; + } + public ping() { + return Promise.resolve(); + } + public version() { + return Promise.resolve(new SemVer('0.0.0-mock')); + } + public defaultValue() { + return Promise.resolve({}); + } + public dump() { + return of(this.cache.config); + } + public sync(events: Array) { + const config: ADCSDK.Configuration = {}; + const resourceTypeToPluralKey = (type: ADCSDK.ResourceType) => + pluralize.plural(type); + return from(events).pipe( + tap((event) => { + if (event.type === ADCSDK.EventType.DELETE) return; + const key = resourceTypeToPluralKey(event.resourceType); + if (!config[key]) config[key] = []; + config[key].push(event.newValue); + }), + toArray(), + switchMap( + () => ( + (this.cache.config = config), + of({ + success: true, + event: {} as ADCSDK.Event, // keep empty + } as ADCSDK.BackendSyncResult) + ), + ), + ); + } + on: () => Subscription; + supportValidate?: () => Promise; + supportStreamRoute?: () => Promise; + } + return new MockBackend(); +}; + +export const jestMockBackend = (mockedBackend?: ADCSDK.Backend) => { + if (!mockedBackend) mockedBackend = mockBackend(); + const originalLoadBackend = commandUtils.loadBackend; + jest + .spyOn(commandUtils, 'loadBackend') + .mockImplementation((backend, opts) => { + if (backend === 'mock') return mockedBackend; + return originalLoadBackend(backend, opts); + }); + return mockedBackend; +}; diff --git a/apps/cli-e2e/jest.config.ts b/apps/cli/jest.config.e2e.ts similarity index 53% rename from apps/cli-e2e/jest.config.ts rename to apps/cli/jest.config.e2e.ts index c7474ce8..62fce50d 100644 --- a/apps/cli-e2e/jest.config.ts +++ b/apps/cli/jest.config.e2e.ts @@ -1,9 +1,9 @@ /* eslint-disable */ export default { - displayName: 'cli-e2e', + displayName: 'cli', preset: '../../jest.preset.js', - globalSetup: '/src/support/global-setup.ts', - globalTeardown: '/src/support/global-teardown.ts', + globalSetup: '/e2e/support/global-setup.ts', + globalTeardown: '/e2e/support/global-teardown.ts', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': [ @@ -14,5 +14,6 @@ export default { ], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/cli-e2e', + coverageDirectory: '../../coverage/libs/backend-api7/e2e', + testMatch: ['**/?(*.)+(e2e-spec).[jt]s?(x)'], }; diff --git a/apps/cli/package.json b/apps/cli/package.json index 71ffc5d0..97c9bf10 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,10 +1,17 @@ { "private": true, "devDependencies": { - "@api7/adc-sdk": "workspace:*", "@api7/adc-backend-api7": "workspace:*", "@api7/adc-backend-apisix": "workspace:*", "@api7/adc-backend-apisix-standalone": "workspace:*", - "@api7/adc-converter-openapi": "workspace:*" + "@api7/adc-converter-openapi": "workspace:*", + "@api7/adc-sdk": "workspace:*", + "@types/express": "^5.0.3", + "@types/supertest": "^6.0.3", + "supertest": "^7.1.4" + }, + "dependencies": { + "body-parser": "^2.2.0", + "express": "^5.1.0" } } diff --git a/apps/cli/project.json b/apps/cli/project.json index 6527e4c7..896af655 100644 --- a/apps/cli/project.json +++ b/apps/cli/project.json @@ -57,6 +57,17 @@ "jestConfig": "apps/cli/jest.config.ts" } }, + "e2e": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/{e2eProjectRoot}" + ], + "options": { + "jestConfig": "apps/cli/jest.config.e2e.ts", + "passWithNoTests": true, + "runInBand": true + } + }, "export-schema": { "executor": "nx:run-commands", "options": { diff --git a/apps/cli/src/command/helper.ts b/apps/cli/src/command/helper.ts index 2c8c9431..800e5415 100644 --- a/apps/cli/src/command/helper.ts +++ b/apps/cli/src/command/helper.ts @@ -7,7 +7,12 @@ import { resolve } from 'node:path'; import parseDuration from 'parse-duration'; import qs from 'qs'; -export class BaseCommand extends Command { +export interface BaseOptions { + verbose: boolean; +} +export class BaseCommand< + OPTS extends BaseOptions = BaseOptions, +> extends Command { private examples: Array<{ title: string; command: string }> = []; constructor(name: string, summary?: string, description?: string) { @@ -49,9 +54,16 @@ export class BaseCommand extends Command { this.description(`${currDescription}\n\n${exampleHeader}\n${exampleText}`); return this; } + + public handle(cb: (opts: OPTS, command: Command) => void | Promise) { + this.action((_, command: Command) => cb(command.opts(), command)); + return this; + } } -export class BackendCommand extends BaseCommand { +export class BackendCommand< + OPTS extends BaseOptions = BaseOptions, +> extends BaseCommand { constructor(name: string, summary?: string, description?: string) { super(name, summary, description); @@ -59,24 +71,17 @@ export class BackendCommand extends BaseCommand { } public handle(cb: (opts: OPTS, command: Command) => void | Promise) { - this.action(async (_, command: Command) => { - const opts = command.opts(); - - if ( - (has(opts, 'tlsClientCertFile') && !has(opts, 'tlsClientKeyFile')) || - (has(opts, 'tlsClientKeyFile') && !has(opts, 'tlsClientCertFile')) - ) { - console.log( - chalk.red( - 'TLS client certificate and key must be provided at the same time', - ), - ); - return; - } + const opts = this.opts(); - await cb(opts, command); - }); - return this; + if (!has(opts, 'tlsClientCertFile') || !has(opts, 'tlsClientKeyFile')) { + console.log( + chalk.red( + 'TLS client certificate and key must be provided at the same time', + ), + ); + return this; + } + return super.handle(cb); } private addBackendOptions() { diff --git a/apps/cli/src/command/index.ts b/apps/cli/src/command/index.ts index f666a564..ac7da5ae 100644 --- a/apps/cli/src/command/index.ts +++ b/apps/cli/src/command/index.ts @@ -6,6 +6,7 @@ import { ConvertCommand } from './convert.command'; import { DevCommand } from './dev.command'; import { DiffCommand } from './diff.command'; import { DumpCommand } from './dump.command'; +import { IngressServerCommand } from './ingress-server.command'; import { IngressSyncCommand } from './ingress-sync.command'; import { LintCommand } from './lint.command'; import { PingCommand } from './ping.command'; @@ -73,6 +74,7 @@ export const setupIngressCommands = (): Command => { } program.addCommand(IngressSyncCommand); + program.addCommand(IngressServerCommand); if (process.env.NODE_ENV === 'development') program.addCommand(DevCommand); diff --git a/apps/cli/src/command/ingress-server.command.ts b/apps/cli/src/command/ingress-server.command.ts new file mode 100644 index 00000000..1e63bb9d --- /dev/null +++ b/apps/cli/src/command/ingress-server.command.ts @@ -0,0 +1,9 @@ +import { ADCServer } from '../server'; +import { BaseCommand } from './helper'; + +export const IngressServerCommand = new BaseCommand('server').handle( + async () => { + await new ADCServer().start(); + console.log('ADC server is running on: http://127.0.0.1:3000'); + }, +); diff --git a/apps/cli/src/server/index.ts b/apps/cli/src/server/index.ts new file mode 100644 index 00000000..54e93702 --- /dev/null +++ b/apps/cli/src/server/index.ts @@ -0,0 +1,27 @@ +import express from 'express'; +import type { Express } from 'express'; +import type { Server } from 'node:http'; + +import { syncHandler } from './sync'; + +export class ADCServer { + private readonly express: Express; + private server?: Server; + + constructor() { + this.express = express(); + this.express.disable('x-powered-by'); + this.express.use(express.json({ limit: '100mb' })); + this.express.put('/sync', syncHandler); + } + + public async start() { + return new Promise((resolve) => { + this.server = this.express.listen(3000, '127.0.0.1', () => resolve()); + }); + } + + public TEST_ONLY_getExpress() { + return this.express; + } +} diff --git a/apps/cli/src/server/schema.ts b/apps/cli/src/server/schema.ts new file mode 100644 index 00000000..fd6f5820 --- /dev/null +++ b/apps/cli/src/server/schema.ts @@ -0,0 +1,16 @@ +import { z } from 'zod'; + +const SyncTask = z.strictObject({ + opts: z.looseObject({ + backend: z.string().min(1), + server: z.url().min(1), + token: z.string().min(1), + lint: z.boolean().optional().default(true), + }), + config: z.looseObject({}), +}); + +export const SyncInput = z.strictObject({ + task: SyncTask, +}); +export type SyncInputType = z.infer; diff --git a/apps/cli/src/server/sync.ts b/apps/cli/src/server/sync.ts new file mode 100644 index 00000000..f7f0743b --- /dev/null +++ b/apps/cli/src/server/sync.ts @@ -0,0 +1,95 @@ +import * as ADCSDK from '@api7/adc-sdk'; +import type { RequestHandler } from 'express'; +import { omit, toString } from 'lodash'; +import { lastValueFrom, toArray } from 'rxjs'; + +import { loadBackend } from '../command/utils'; +import { DifferV3 } from '../differ/differv3'; +import { check } from '../linter'; +import { SyncInput, type SyncInputType } from './schema'; + +export const syncHandler: RequestHandler< + unknown, + unknown, + SyncInputType +> = async (req, res) => { + const parsedInput = SyncInput.safeParse(req.body); + if (!parsedInput.success) + return res.status(400).json({ + message: parsedInput.error.message, + errors: parsedInput.error.issues, + }); + const { task } = parsedInput.data; + + // load local configuration and validate it + const local = task.config as ADCSDK.Configuration; + if (task.opts.lint) { + const result = check(local); + if (!result.success) + return res.status(400).json({ + message: result.error.message, + errors: result.error.issues, + }); + } + + try { + // load remote configuration and diff with local + const backend = loadBackend(task.opts.backend, { ...task.opts }); + const remote = await lastValueFrom(backend.dump()); + const diff = DifferV3.diff(local, remote, await backend.defaultValue()); + + // sync the diff + const results = await lastValueFrom( + backend + .sync(diff, { + exitOnFailure: false, + concurrent: 4, + }) + .pipe(toArray()), + ); + const successes = results.filter((result) => result.success); + const faileds = results.filter((result) => !result.success); + const simplifyEvent = (event: ADCSDK.Event) => + omit(event, ['diff', 'oldValue', 'newValue', 'subEvents']); + const output = { + status: + results.length === successes.length + ? 'success' + : results.length === faileds.length + ? 'all_failed' + : 'partial_failure', + total_resources: results.length, + success_count: successes.length, + failed_count: faileds.length, + success: [ + ...successes.map(({ event, axiosResponse }) => ({ + event: simplifyEvent(event), + synced_at: new Date( + axiosResponse?.headers?.date ?? new Date(), + ).toISOString(), + })), + ], + failed: [ + ...faileds.map(({ event, error, axiosResponse }) => ({ + event: simplifyEvent(event), + failed_at: new Date( + axiosResponse?.headers?.date ?? new Date(), + ).toISOString(), + reason: error.message, + ...(axiosResponse && { + response: { + status: axiosResponse.status, + headers: axiosResponse.headers, + data: axiosResponse.data, + }, + }), + })), + ], + }; + res.status(202).json(output); + } catch (err) { + res.status(500).json({ + message: toString(err), + }); + } +}; diff --git a/apps/cli/tsconfig.spec.json b/apps/cli/tsconfig.spec.json index 9b2a121d..8572be44 100644 --- a/apps/cli/tsconfig.spec.json +++ b/apps/cli/tsconfig.spec.json @@ -2,13 +2,20 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] + "module": "nodenext", + "types": [ + "jest", + "node" + ] }, "include": [ "jest.config.ts", + "src/**/*.ts", "src/**/*.test.ts", "src/**/*.spec.ts", - "src/**/*.d.ts" + "src/**/*.d.ts", + "e2e/support/**/*.ts", + "e2e/**/*.e2e-spec.ts", + "src/linter/**/*.ts" ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1feae037..9b1edd06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -206,6 +206,13 @@ importers: version: 5.98.0(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12) apps/cli: + dependencies: + body-parser: + specifier: ^2.2.0 + version: 2.2.0 + express: + specifier: ^5.1.0 + version: 5.1.0 devDependencies: '@api7/adc-backend-api7': specifier: workspace:* @@ -222,6 +229,15 @@ importers: '@api7/adc-sdk': specifier: workspace:* version: link:../../libs/sdk + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + '@types/supertest': + specifier: ^6.0.3 + version: 6.0.3 + supertest: + specifier: ^7.1.4 + version: 7.1.4 libs/backend-api7: devDependencies: @@ -1257,6 +1273,10 @@ packages: '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1370,6 +1390,9 @@ packages: '@nx/workspace@21.2.4': resolution: {integrity: sha512-NX/dADO0JqcS0FAIJT9E9xpvAmP1vriyP4lBaYQxxiAeN4Tg0CMg4KxYIkiq7cAyvj/2Wjchrt8kbHyMmEJcUA==} + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -1736,6 +1759,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/deep-diff@1.0.5': resolution: {integrity: sha512-PQyNSy1YMZU1hgZA5tTYfHPpUAo9Dorn1PZho2/budQLfqLu3JIP37JAavnwYpR1S2yFZTXa3hxaE4ifGW5jaA==} @@ -1751,9 +1777,15 @@ packages: '@types/express-serve-static-core@4.19.1': resolution: {integrity: sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==} + '@types/express-serve-static-core@5.0.7': + resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -1784,6 +1816,9 @@ packages: '@types/lodash@4.17.4': resolution: {integrity: sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==} + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -1829,6 +1864,12 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -2023,6 +2064,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2143,6 +2188,9 @@ packages: resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} engines: {node: '>=12'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} @@ -2247,6 +2295,10 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + bonjour-service@1.2.1: resolution: {integrity: sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==} @@ -2447,6 +2499,9 @@ packages: common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -2469,6 +2524,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -2479,10 +2538,17 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.7.1: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} @@ -2725,6 +2791,9 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2855,6 +2924,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -2970,6 +3043,10 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2983,6 +3060,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -3024,6 +3104,10 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-cache-dir@4.0.0: resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} engines: {node: '>=14.16'} @@ -3083,6 +3167,14 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -3094,6 +3186,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + front-matter@4.0.2: resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} @@ -3224,6 +3320,10 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -3416,6 +3516,9 @@ packages: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -3838,6 +3941,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} @@ -3849,6 +3956,10 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3872,15 +3983,28 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} hasBin: true + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3950,6 +4074,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -4157,6 +4285,10 @@ packages: path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4480,6 +4612,10 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -4588,6 +4724,10 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + run-applescript@7.0.0: resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} @@ -4793,6 +4933,10 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -4804,6 +4948,10 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + setprototypeof@1.1.0: resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} @@ -5006,6 +5154,14 @@ packages: engines: {node: '>=16'} hasBin: true + superagent@10.2.3: + resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} + engines: {node: '>=14.18.0'} + + supertest@7.1.4: + resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==} + engines: {node: '>=14.18.0'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -5205,6 +5361,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-assert@1.0.9: resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} @@ -6656,6 +6816,8 @@ snapshots: '@emnapi/runtime': 1.3.1 '@tybys/wasm-util': 0.9.0 + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6949,6 +7111,10 @@ snapshots: - '@swc/core' - debug + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -7312,6 +7478,8 @@ snapshots: dependencies: '@types/node': 22.16.5 + '@types/cookiejar@2.1.5': {} + '@types/deep-diff@1.0.5': {} '@types/eslint-scope@3.7.7': @@ -7333,6 +7501,13 @@ snapshots: '@types/range-parser': 1.2.7 '@types/send': 0.17.4 + '@types/express-serve-static-core@5.0.7': + dependencies: + '@types/node': 22.16.5 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 @@ -7340,6 +7515,12 @@ snapshots: '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 5.0.7 + '@types/serve-static': 1.15.7 + '@types/graceful-fs@4.1.9': dependencies: '@types/node': 22.16.5 @@ -7371,6 +7552,8 @@ snapshots: '@types/lodash@4.17.4': {} + '@types/methods@1.1.4': {} + '@types/mime@1.3.5': {} '@types/node-forge@1.3.11': @@ -7400,7 +7583,7 @@ snapshots: '@types/serve-index@1.9.4': dependencies: - '@types/express': 4.17.21 + '@types/express': 5.0.3 '@types/serve-static@1.15.7': dependencies: @@ -7418,6 +7601,18 @@ snapshots: '@types/stack-utils@2.0.3': {} + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 22.16.5 + form-data: 4.0.4 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + '@types/ws@8.5.10': dependencies: '@types/node': 22.16.5 @@ -7692,6 +7887,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.11.3): dependencies: acorn: 8.11.3 @@ -7780,6 +7980,8 @@ snapshots: array-union@3.0.1: {} + asap@2.0.6: {} + async@3.2.5: {} asynckit@0.4.0: {} @@ -7940,6 +8142,20 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + bonjour-service@1.2.1: dependencies: fast-deep-equal: 3.1.3 @@ -8129,6 +8345,8 @@ snapshots: common-path-prefix@3.0.0: {} + component-emitter@1.3.1: {} + compressible@2.0.18: dependencies: mime-db: 1.52.0 @@ -8155,14 +8373,22 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + content-type@1.0.5: {} convert-source-map@2.0.0: {} cookie-signature@1.0.6: {} + cookie-signature@1.2.2: {} + cookie@0.7.1: {} + cookiejar@2.1.4: {} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 @@ -8391,6 +8617,11 @@ snapshots: transitivePeerDependencies: - supports-color + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + diff-sequences@29.6.3: {} diff@4.0.2: {} @@ -8506,6 +8737,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -8692,6 +8930,38 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -8706,6 +8976,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-safe-stringify@2.1.1: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -8754,6 +9026,17 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-cache-dir@4.0.0: dependencies: common-path-prefix: 3.0.0 @@ -8818,12 +9101,28 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + forwarded@0.2.0: {} fraction.js@4.3.7: {} fresh@0.5.2: {} + fresh@2.0.0: {} + front-matter@4.0.2: dependencies: js-yaml: 3.14.1 @@ -8956,6 +9255,10 @@ snapshots: has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -9116,6 +9419,8 @@ snapshots: is-plain-obj@3.0.0: {} + is-promise@4.0.0: {} + is-stream@2.0.1: {} is-unicode-supported@0.1.0: {} @@ -9727,6 +10032,8 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@3.5.3: dependencies: fs-monkey: 1.0.6 @@ -9740,6 +10047,8 @@ snapshots: merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -9758,12 +10067,20 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mime@1.6.0: {} + mime@2.6.0: {} + mimic-fn@2.1.0: {} mini-css-extract-plugin@2.4.7(webpack@5.99.9(@swc/core@1.5.7(@swc/helpers@0.5.12))(esbuild@0.19.12)): @@ -9820,6 +10137,8 @@ snapshots: negotiator@0.6.3: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} no-case@3.0.4: @@ -10059,6 +10378,8 @@ snapshots: path-to-regexp@0.1.12: {} + path-to-regexp@8.2.0: {} + path-type@4.0.0: {} picocolors@1.0.1: {} @@ -10345,6 +10666,13 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + react-is@18.3.1: {} react-refresh@0.10.0: {} @@ -10446,6 +10774,16 @@ snapshots: dependencies: glob: 7.2.3 + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + run-applescript@7.0.0: {} run-parallel@1.2.0: @@ -10617,6 +10955,22 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -10642,6 +10996,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + setprototypeof@1.1.0: {} setprototypeof@1.2.0: {} @@ -10859,6 +11222,27 @@ snapshots: transitivePeerDependencies: - supports-color + superagent@10.2.3: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.1 + fast-safe-stringify: 2.1.1 + form-data: 4.0.4 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supertest@7.1.4: + dependencies: + methods: 1.1.2 + superagent: 10.2.3 + transitivePeerDependencies: + - supports-color + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -11061,6 +11445,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-assert@1.0.9: {} typescript@5.8.3: {}