diff --git a/@xen-orchestra/rest-api/.eslintrc.json b/@xen-orchestra/rest-api/.eslintrc.json new file mode 100644 index 00000000000..2b36e77bce0 --- /dev/null +++ b/@xen-orchestra/rest-api/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "no-extra-semi": "off", + "no-use-before-define": "off", + "@typescript-eslint/no-explicit-any": "off" + } + } + ], + "ignorePatterns": ["src/open-api/routes/routes.ts"] +} diff --git a/@xen-orchestra/rest-api/.gitignore b/@xen-orchestra/rest-api/.gitignore new file mode 100644 index 00000000000..136f4c199db --- /dev/null +++ b/@xen-orchestra/rest-api/.gitignore @@ -0,0 +1 @@ +src/open-api/* diff --git a/@xen-orchestra/rest-api/package.json b/@xen-orchestra/rest-api/package.json new file mode 100644 index 00000000000..f130208da05 --- /dev/null +++ b/@xen-orchestra/rest-api/package.json @@ -0,0 +1,34 @@ +{ + "author": "Vates", + "name": "@xen-orchestra/rest-api", + "version": "0.1.0", + "description": "", + "main": "./dist/index.js", + "license": "MIT", + "private": true, + "type": "module", + "engines": { + "node": ">=20" + }, + "scripts": { + "build": "tsoa spec-and-routes && tsc", + "dev": "tsoa spec-and-routes && tsc --watch" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.10.6", + "@types/swagger-ui-express": "^4.1.7", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", + "eslint": "^9.18.0", + "typescript": "~5.6.3" + }, + "dependencies": { + "express": "^4.21.2", + "inversify": "^6.2.1", + "inversify-binding-decorators": "^4.0.0", + "swagger-ui-express": "^5.0.1", + "tsoa": "^6.6.0", + "xo-common": "^0.8.0" + } +} diff --git a/@xen-orchestra/rest-api/src/abstract/xapi-xo.controller.ts b/@xen-orchestra/rest-api/src/abstract/xapi-xo.controller.ts new file mode 100644 index 00000000000..e957762c0e5 --- /dev/null +++ b/@xen-orchestra/rest-api/src/abstract/xapi-xo.controller.ts @@ -0,0 +1,30 @@ +import { getRestApi } from '../index.js' +import { XapiXoObject } from '../xoApp.type.js' +import { Controller } from 'tsoa' + +export abstract class XapiXoController extends Controller { + protected restApi = getRestApi() + protected type + + constructor(type: T['type']) { + super() + this.type = type + } + + // wrap these methods with permission (filter only objects that the users can see) + protected getObjects(): Record | undefined { + return this.restApi.getObjectsByType(this.type) as Record | undefined + } + protected getById(id: T['id']): T { + return this.restApi.getObject(id, this.type) + } + + protected getXapiObject(objOrId: T['id'] | T) { + return this.restApi.getXapiObject(objOrId, this.type) + } + // -------------------------------------------------------------------------------- + + protected getObjectIds(): T['id'][] { + return Object.keys(this.getObjects() ?? {}) + } +} diff --git a/@xen-orchestra/rest-api/src/abstract/xo.controller.ts b/@xen-orchestra/rest-api/src/abstract/xo.controller.ts new file mode 100644 index 00000000000..cfc0252c715 --- /dev/null +++ b/@xen-orchestra/rest-api/src/abstract/xo.controller.ts @@ -0,0 +1,37 @@ +import { getRestApi } from '../index.js' +import { NonXapiObject } from '../xoApp.type.js' +import { Controller } from 'tsoa' + +type XoType = 'server' + +export abstract class XoController extends Controller { + #restApi = getRestApi() + #type: XoType + + #fnByType = { + server: { + getAll: () => this.#restApi.getServers(), + getById: (id: NonXapiObject['id']) => this.#restApi.getServer(id), + }, + } + + constructor(type: XoType) { + super() + this.#type = type + } + + protected getObjects() { + return this.#fnByType[this.#type].getAll() + } + + protected getObject(id: T['id']) { + return this.#fnByType[this.#type].getById(id) + } + protected async getObjectIds(): Promise { + const objects = await this.getObjects() + if (Array.isArray(objects)) { + return objects.map(obj => obj.id) + } + return Object.keys(objects) + } +} diff --git a/@xen-orchestra/rest-api/src/dashboard/dashboard.controller.ts b/@xen-orchestra/rest-api/src/dashboard/dashboard.controller.ts new file mode 100644 index 00000000000..d0c3e479133 --- /dev/null +++ b/@xen-orchestra/rest-api/src/dashboard/dashboard.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Get, Request, Route } from 'tsoa' +import DashboardService from './dashboard.service.js' +import { inject } from 'inversify' +import { provideSingleton } from '../ioc/helper.js' +import { Request as ExReq } from 'express' + +export type Dashboard = { + vmsStatus: { + running: number + inactive: number + unknown: number + } + poolsStatus: { + connected: number + unreachable: number + unknown: number + } +} + +@Route('dashboard') +@provideSingleton(DashboardController) +export class DashboardController extends Controller { + #dashboardService + constructor(@inject(DashboardService) dashboardService: DashboardService) { + super() + this.#dashboardService = dashboardService + } + + @Get() + public async getDashboard(@Request() req: ExReq): Promise { + const resp = req.res! + resp.setHeader('Access-Control-Allow-Origin', '*') // TODO: remove this. Only used for test + + return this.#dashboardService.getDashboard() + } +} diff --git a/@xen-orchestra/rest-api/src/dashboard/dashboard.service.ts b/@xen-orchestra/rest-api/src/dashboard/dashboard.service.ts new file mode 100644 index 00000000000..3d61abc882f --- /dev/null +++ b/@xen-orchestra/rest-api/src/dashboard/dashboard.service.ts @@ -0,0 +1,107 @@ +import { getRestApi } from '../index.js' +import { Dashboard } from './dashboard.controller.js' +import { provideSingleton } from '../ioc/helper.js' + +// cache used to compare changes and trigger dashboard events +type CacheDashboard = { + vmsStatus: Dashboard['vmsStatus'] + poolsStatus: Dashboard['poolsStatus'] +} +const cache = new Map() + +@provideSingleton(DashboardService) +export default class DashboardService { + #restApi + #fnById = { + // arrow fn to ensure to call the method in the good context + vmsStatus: () => this.#getVmsStatus(), + poolsStatus: () => this.#getPoolsStatus(), + } + + constructor() { + this.#restApi = getRestApi() + this.#registerListener() + } + + async getDashboard(): Promise { + const vmsStatus = this.#getVmsStatus() + const poolsStatus = await this.#getPoolsStatus() + + return { vmsStatus, poolsStatus } + } + + #getVmsStatus(): Dashboard['vmsStatus'] { + const vms = Object.values(this.#restApi.getObjectsByType('VM') ?? {}) as any + let running = 0 + let inactive = 0 + let unknown = 0 + + vms.forEach(vm => { + if (vm.power_state === 'Running' || vm.power_state === 'Paused') { + running++ + } else if (vm.power_state === 'Halted' || vm.power_state === 'Suspended') { + inactive++ + } else { + unknown++ + } + }) + + const result = { running, inactive, unknown } + cache.set('vmsStatus', result) + + return result + } + + async #getPoolsStatus(): Promise { + const servers = await this.#restApi.getServers() + const poolIds = Object.keys(this.#restApi.getObjectsByType('pool') ?? {}) + + let nConnectedServers = 0 + let nUnreachableServers = 0 + let nUnknownServers = 0 + servers.forEach(server => { + // it may happen that some servers are marked as "connected", but no pool matches "server.pool" + // so they are counted as `nUnknownServers` + if (server.status === 'connected' && poolIds.includes(server.poolId)) { + nConnectedServers++ + return + } + + if ( + server.status === 'disconnected' && + server.error !== undefined && + server.error.connectedServerId === undefined + ) { + nUnreachableServers++ + return + } + + if (server.status === 'disconnected') { + return + } + + nUnknownServers++ + }) + + const result = { connected: nConnectedServers, unreachable: nUnreachableServers, unknown: nUnknownServers } + cache.set('poolsStatus', result) + + return result + } + + async #maybeSendEvent(key: keyof Dashboard) { + const stringifiedCacheValue = JSON.stringify(cache.get(key)) + const newValue = await this.#fnById[key]() + if (JSON.stringify(newValue) === stringifiedCacheValue) { + return + } + + this.#restApi.sendData(key, 'dashboard', newValue, 'update') + } + + #registerListener() { + this.#restApi.ee.on('vm', () => this.#maybeSendEvent('vmsStatus')) + this.#restApi.ee.on('pool', () => this.#maybeSendEvent('poolsStatus')) + // this.#restApi.ee.on('host', () => this.#maybeSendEvent('hostsStatus')) + } +} diff --git a/@xen-orchestra/rest-api/src/events/event.controller.ts b/@xen-orchestra/rest-api/src/events/event.controller.ts new file mode 100644 index 00000000000..0d40166e223 --- /dev/null +++ b/@xen-orchestra/rest-api/src/events/event.controller.ts @@ -0,0 +1,34 @@ +import { Request as ExRequest } from 'express' +import { getRestApi } from '../index.js' +import { Controller, Get, Request, Route } from 'tsoa' +import { provideSingleton } from '../ioc/helper.js' + +@Route('events') +@provideSingleton(EventsController) +export class EventsController extends Controller { + /** + * subscribe to all evenements + */ + @Get() + public async getEvent(@Request() req: ExRequest): Promise { + const restApi = getRestApi() + + const res = req.res! + + res.setHeader('Access-Control-Allow-Origin', '*') // TODO: remove this. Only used for test + + res.setHeader('Content-Type', 'text/event-stream') + res.setHeader('Cache-Control', 'no-cache') + res.setHeader('Connection', 'keep-alive') + + res.write(`data:${JSON.stringify({ data: null, operation: 'initial' })}\n\n`) + + const symbol = Symbol('client-id') + restApi.addSseClient(symbol, res) + + res.on('close', () => { + restApi.removeSseClient(symbol) + res.end() + }) + } +} diff --git a/@xen-orchestra/rest-api/src/index.ts b/@xen-orchestra/rest-api/src/index.ts new file mode 100644 index 00000000000..6fe72eb775f --- /dev/null +++ b/@xen-orchestra/rest-api/src/index.ts @@ -0,0 +1,134 @@ +import swaggerUi from 'swagger-ui-express' +import { Express, NextFunction, Request, Response } from 'express' + +import swaggerOpenApiSpec from './open-api/spec/swagger.json' assert { type: 'json' } +import { RegisterRoutes } from './open-api/routes/routes.js' +import { XapiXoObject, XoApp } from './xoApp.type.js' +import { EventEmitter } from 'events' +import DashboardService from './dashboard/dashboard.service.js' +import { iocContainer } from './ioc/ioc.js' +import { errorHandler } from './middleware/error.middleware.js' +import fs from 'node:fs/promises' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) + +class RestApi { + #sseClients: Map = new Map() + #express + + ee = new EventEmitter() + + authenticateUser + getObject + getObjects + getServers: XoApp['getAllXenServers'] + getServer: XoApp['getXenServer'] + getXapiObject: XoApp['getXapiObject'] + getObjectsByType + + constructor(xoApp: XoApp, express: Express) { + console.log('INITIALIZE') + if (restApi !== undefined) { + throw new Error('RestApi is a singleton') + } + + this.#express = express + + // existing xo methods + this.authenticateUser = xoApp.authenticateUser.bind(xoApp) + this.getObject = xoApp.getObject.bind(xoApp) + this.getObjects = xoApp.getObjects.bind(xoApp) + this.getServers = () => xoApp.getAllXenServers() + this.getXapiObject = (idOrObj, type) => xoApp.getXapiObject(idOrObj, type) + this.getServer = id => xoApp.getXenServer(id) + + // helpers + this.getObjectsByType = (type: T) => + xoApp.objects.indexes.type[type] + + // @ts-ignore + this.#registerListener(xoApp.objects.allIndexes.type) + } + + addSseClient(id: symbol, client: Response) { + this.#sseClients.set(id, client) + } + + removeSseClient(id: symbol) { + this.#sseClients.delete(id) + } + + sendData(objId: string, objType: string | undefined, obj: any | undefined, operation: 'update' | 'add' | 'remove') { + this.#sseClients.forEach(client => { + client.write(`data: ${JSON.stringify({ id: objId, type: objType, data: obj, operation })}\n\n`) + }) + } + + async registerRoute( + endpoint: string, + cb: (req: Request, res: Response, next: NextFunction) => any, + openapiSpec: Object, + httpMethod: 'get' | 'post' | 'put' | 'patch' | 'delete' = 'get' + ) { + if (endpoint[0] !== '/') { + endpoint = '/' + endpoint + } + this.#express[httpMethod](`/rest/v1${endpoint}`, cb) + const currentConfig = JSON.parse( + await fs.readFile(`${__dirname}/open-api/spec/swagger-tmp.json`, { encoding: 'utf-8' }) + ) + currentConfig.paths[endpoint] = openapiSpec + await fs.writeFile(`${__dirname}/open-api/spec/swagger-tmp.json`, JSON.stringify(currentConfig), { + encoding: 'utf-8', + }) + } + + #registerListener(obj: EventEmitter) { + // XO events + obj.on('remove', (type, data) => this.#handleXapiEvent(type, data, 'remove')) + obj.on('add', (type, data) => this.#handleXapiEvent(type, data, 'add')) + obj.on('update', (type, data) => this.#handleXapiEvent(type, data, 'update')) + } + + async #handleXapiEvent(type: string, data: XapiXoObject, operation: 'update' | 'add' | 'remove') { + if ((data as any).type === 'message') { + // just to avoid client to be spammed by message for the moment + return + } + + this.ee.emit(type.toLowerCase(), data, operation) + this.sendData(data.id, data.type, data, operation) + } +} + +let restApi: RestApi +export const getRestApi = () => { + if (restApi === undefined) { + throw new Error('The REST API is not instantiated') + } + return restApi +} + +export default async function setupRestApi(express: Express, xoApp: XoApp) { + restApi = new RestApi(xoApp, express) + + await fs.copyFile(`${__dirname}/open-api/spec/swagger.json`, `${__dirname}/open-api/spec/swagger-tmp.json`) + + express.use((req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*') // TODO: remove this. Only used for test + next() + }) + + express.use('/rest/v1/api-doc', swaggerUi.serve, async (req, res, next) => { + const spec = JSON.parse(await fs.readFile(`${__dirname}/open-api/spec/swagger-tmp.json`, { encoding: 'utf-8' })) + swaggerUi.setup(spec)(req, res, next) + }) + RegisterRoutes(express) + + express.use(errorHandler) + + // in order to create the instance of the service (and start to listen for dashboard changes) + iocContainer.get(DashboardService) +} diff --git a/@xen-orchestra/rest-api/src/ioc/helper.ts b/@xen-orchestra/rest-api/src/ioc/helper.ts new file mode 100644 index 00000000000..b2a89925b72 --- /dev/null +++ b/@xen-orchestra/rest-api/src/ioc/helper.ts @@ -0,0 +1,8 @@ +import { interfaces } from 'inversify' +import { fluentProvide, provide } from 'inversify-binding-decorators' + +const provideSingleton = function (identifier: interfaces.ServiceIdentifier) { + return fluentProvide(identifier).inSingletonScope().done() +} + +export { provideSingleton, provide } diff --git a/@xen-orchestra/rest-api/src/ioc/ioc.ts b/@xen-orchestra/rest-api/src/ioc/ioc.ts new file mode 100644 index 00000000000..545e51920f5 --- /dev/null +++ b/@xen-orchestra/rest-api/src/ioc/ioc.ts @@ -0,0 +1,11 @@ +import { Container, decorate, injectable } from 'inversify' +import { buildProviderModule } from 'inversify-binding-decorators' +import { Controller } from 'tsoa' + +const iocContainer = new Container() + +decorate(injectable(), Controller) + +iocContainer.load(buildProviderModule()) + +export { iocContainer } diff --git a/@xen-orchestra/rest-api/src/middleware/authentication.ts b/@xen-orchestra/rest-api/src/middleware/authentication.ts new file mode 100644 index 00000000000..b6c0b520bad --- /dev/null +++ b/@xen-orchestra/rest-api/src/middleware/authentication.ts @@ -0,0 +1,39 @@ +import * as express from 'express' +import { getRestApi } from '../index.js' + +export async function expressAuthentication( + req: express.Request, + securityName: string, + scopes?: string[] +): Promise { + const restApi = getRestApi() + const ip = req.ip + const permission = scopes?.[0] + + if (securityName === 'token') { + const token = req.cookies.token ?? req.cookies.authenticationToken + + const { user } = await restApi.authenticateUser({ token }, { ip }) + if (permission !== undefined && permission !== user.permission) { + return Promise.reject(new Error('unauthorize')) + } + return Promise.resolve(user) + } + + if (securityName === 'basic_auth') { + const authorization = req.headers.authorization ?? '' + const [, encodedCredentials] = authorization.split(' ') + if (encodedCredentials === undefined) { + return Promise.reject(new Error('missing credentials')) + } + + const [username, password] = Buffer.from(encodedCredentials, 'base64').toString().split(':') + const { user } = await restApi.authenticateUser({ username, password }, { ip }) + if (permission !== undefined && permission !== user.permission) { + return Promise.reject(new Error('unauthorize')) + } + return Promise.resolve(user) + } + + throw new Error('unimplemented') +} diff --git a/@xen-orchestra/rest-api/src/middleware/error.middleware.ts b/@xen-orchestra/rest-api/src/middleware/error.middleware.ts new file mode 100644 index 00000000000..c86b28804d3 --- /dev/null +++ b/@xen-orchestra/rest-api/src/middleware/error.middleware.ts @@ -0,0 +1,15 @@ +import { noSuchObject } from 'xo-common/api-errors.js' + +import { NextFunction, Request, Response } from 'express' + +export function errorHandler(err: Error, _req: Request, res: Response, next: NextFunction) { + console.error(err) // use log.error instead + if (noSuchObject.is(err)) { + res.status(404).json({ error: err.message }) + return next() + } + + res.json({ error: err.message }) + + next() +} diff --git a/@xen-orchestra/rest-api/src/servers/server.controller.ts b/@xen-orchestra/rest-api/src/servers/server.controller.ts new file mode 100644 index 00000000000..d31f0d7ac72 --- /dev/null +++ b/@xen-orchestra/rest-api/src/servers/server.controller.ts @@ -0,0 +1,24 @@ +import { Get, Path, Route, Security } from 'tsoa' +import { XoServer } from './server.type.js' +import { provideSingleton } from '../ioc/helper.js' +import { XoController } from '../abstract/xo.controller.js' + +@Route('servers') +@provideSingleton(ServersController) +export class ServersController extends XoController { + constructor() { + super('server') + } + + @Security('token', ['admin']) + @Get() + public getServers(): Promise { + return this.getObjectIds() + } + + @Security('basic_auth', ['admin']) + @Get('{id}') + public getServer(@Path() id: XoServer['id']): Promise { + return this.getObject(id) + } +} diff --git a/@xen-orchestra/rest-api/src/servers/server.type.ts b/@xen-orchestra/rest-api/src/servers/server.type.ts new file mode 100644 index 00000000000..b6d352bbfb9 --- /dev/null +++ b/@xen-orchestra/rest-api/src/servers/server.type.ts @@ -0,0 +1,14 @@ +import { NonXapiObject } from '../xoApp.type.js' + +export interface XoServer extends NonXapiObject { + allowUnauthorized: boolean + host: string + label: string + password: string + username: string + enabled: boolean + readOnly: boolean + status: 'connected' | 'disconnected' + error?: any + poolId: string /* XoPool['id'] */ +} diff --git a/@xen-orchestra/rest-api/src/srs/sr.controller.ts b/@xen-orchestra/rest-api/src/srs/sr.controller.ts new file mode 100644 index 00000000000..8223dc4c627 --- /dev/null +++ b/@xen-orchestra/rest-api/src/srs/sr.controller.ts @@ -0,0 +1,57 @@ +import { FormField, Get, Middlewares, Path, Post, Put, Query, Request, Response, Route, UploadedFile } from 'tsoa' +import { Request as ExRequest } from 'express' + +import { provideSingleton } from '../ioc/helper.js' +import { XapiXoController } from '../abstract/xapi-xo.controller.js' + +/** + * Return a task ID or T if ?watch=true + */ +type WatchAction = string | T + +@Route('srs') +@provideSingleton(SrsController) +export class SrsController extends XapiXoController { + constructor() { + super('SR') + } + /** + * Some description + */ + @Get() + @Response(401, 'unautorhized') + public getSrs(): string[] { + return this.getObjectIds() + } + + @Get('{id}') + @Response(404, 'Not found') + @Response(401, 'unautorhized') + public getSr(@Path() id: string) { + return this.getById(id) + } + + @Post('{id}/actions/import_vdi') + public async importVdi( + @Request() req: ExRequest & { length: number }, + @Path() id: string, + @Query() name_label: string, + @Query() name_description: string, + @Query() format: 'raw' | 'vhd' + ): Promise> { + // create task and add the possibility to watch/await the task + const sr = this.restApi.getXapiObject(id, this.type) as any + const length = req.headers['content-length'] + if (length !== undefined) { + req.length = Number(length) + } + + const vdiRef = await sr.$importVdi(req, { + format, + name_label, + name_description, + }) + const vdiUUid = await sr.$xapi.getField('VDI', vdiRef, 'uuid') + return vdiUUid + } +} diff --git a/@xen-orchestra/rest-api/src/vdi/vdi.controller.ts b/@xen-orchestra/rest-api/src/vdi/vdi.controller.ts new file mode 100644 index 00000000000..4f075a9e063 --- /dev/null +++ b/@xen-orchestra/rest-api/src/vdi/vdi.controller.ts @@ -0,0 +1,50 @@ +import { FormField, Get, Middlewares, Path, Post, Put, Query, Request, Res, Response, Route, UploadedFile } from 'tsoa' +import { Request as ExRequest, Response as ExResp } from 'express' + +import { provideSingleton } from '../ioc/helper.js' +import { XapiXoController } from '../abstract/xapi-xo.controller.js' +import { pipeline } from 'node:stream/promises' + +@Route('vdis') +@provideSingleton(VdiController) +export class VdiController extends XapiXoController { + constructor() { + super('VDI') + } + /** + * Some description + */ + @Get() + @Response(401, 'unautorhized') + public getVdis(): string[] { + return this.getObjectIds() + } + + @Get('{id}.{format}') + public async exportVdi( + @Request() req: ExRequest, + @Path() id: string, + @Path() format: 'vhd' | 'raw', + @Query() nbdConcurrency: number, + @Query() perferNbd = false + ) { + const res = req.res! + const vdi = this.getXapiObject(id) as any + const stream = await vdi.$exportContent({ format, perferNbd, nbdConcurrency }) + const headers = { 'content-disposition': 'attachment', 'content-length': undefined } + + const { length } = stream + if (length !== undefined) { + headers['content-length'] = length + } + res.writeHead(200, 'OK', headers) + await pipeline(stream, res) + } + + @Get('{id}') + @Response(404, 'Not found') + @Response(401, 'unautorhized') + public getVdi(@Path() id: string) { + return this.getById(id) + } +} diff --git a/@xen-orchestra/rest-api/src/vms/vm.controller.ts b/@xen-orchestra/rest-api/src/vms/vm.controller.ts new file mode 100644 index 00000000000..2440d72be80 --- /dev/null +++ b/@xen-orchestra/rest-api/src/vms/vm.controller.ts @@ -0,0 +1,50 @@ +import { Get, Path, Post, Query, Response, Route } from 'tsoa' + +import { XapiVm, XoVm } from './vm.type.js' +import { provideSingleton } from '../ioc/helper.js' +import { XapiXoController } from '../abstract/xapi-xo.controller.js' + +interface ApiError { + error: string +} + +@Route('vms') +@provideSingleton(VmsController) +export class VmsController extends XapiXoController { + constructor() { + super('VM') + } + /** + * Some description + */ + @Get() + @Response(401, 'unautorhized') + public getVms(): XoVm['id'][] { + return this.getObjectIds() + } + + @Get('{id}') + @Response(404, 'Not found') + @Response(401, 'unautorhized') + public getVm(@Path() id: XoVm['id']) { + return this.getById(id) + } + + @Post('{id}/actions/start') + @Response(404, 'Not found') + @Response(401, 'unautorhized') + public startVm(@Path() id: XoVm['id']): void { + const vm = this.restApi.getXapiObject(id, this.type) as XapiVm + // create task and add the possibility to watch/await the task + vm.$callAsync('start', false, false).catch(() => {}) + } + + @Post('{id}/actions/shutdown') + @Response(404, 'Not found') + @Response(401, 'unautorhized') + public stopVm(@Path() id: XoVm['id'], @Query() hard?: boolean): void { + const vm = this.restApi.getXapiObject(id, this.type) as XapiVm + // create task and add the possibility to watch/await the task + vm.$callAsync(`${hard ? 'hard' : 'clean'}_shutdown`).catch(() => {}) + } +} diff --git a/@xen-orchestra/rest-api/src/vms/vm.type.ts b/@xen-orchestra/rest-api/src/vms/vm.type.ts new file mode 100644 index 00000000000..a43e84f79bd --- /dev/null +++ b/@xen-orchestra/rest-api/src/vms/vm.type.ts @@ -0,0 +1,15 @@ +import { XapiObject, XapiXoObject } from '../xoApp.type.js' + +export interface XapiVm extends XapiObject { + uuid: string + name_label: string + power_state: 'Running' | 'Paused' | 'Halted' | 'Suspended' +} + +export interface XoVm extends XapiXoObject { + type: 'VM' + + name_label: XapiVm['name_label'] + uuid: XapiVm['uuid'] + power_state: XapiVm['power_state'] +} diff --git a/@xen-orchestra/rest-api/src/xoApp.type.ts b/@xen-orchestra/rest-api/src/xoApp.type.ts new file mode 100644 index 00000000000..1936001b9e0 --- /dev/null +++ b/@xen-orchestra/rest-api/src/xoApp.type.ts @@ -0,0 +1,71 @@ +import EventEmitter from 'events' +import { XoVm } from './vms/vm.type.js' +import { Response } from 'express' +import { XoServer } from './servers/server.type.js' + +// ----- +interface XoPool extends XapiXoObject { + id: string + type: 'pool' +} + +interface XoUser extends NonXapiObject { + id: string + permission: string +} +// ----- +export type XapiObject = { + $ref: string + $id: string + $type: 'VM' | 'pool' + $xapi: any + $call: (method: string, ...args: any) => Promise + $callAsync: (method: string, ...args: any) => Promise +} +/** + * XapiXoObject can be every "xapi-to-xo" object + */ +export type XapiXoObject = { + id: string + type: XapiObject['$type'] +} + +/** + * Every object that is not aa XapiXoObject + */ +export type NonXapiObject = { + id: string +} + +/** + * Can be every type of object handled by XO + */ +export type XoObject = XapiXoObject | NonXapiObject + +interface Objects extends EventEmitter { + indexes: { + type: { + [K in T['type']]: Record> | undefined + } + } +} + +export interface XoApp extends EventEmitter { + authenticateUser: ( + params: { token?: string; username?: string; password?: string }, + optional?: { ip?: string } + ) => Promise<{ bypassOtp: boolean; user: XoUser }> + objects: Objects /** Nedd to add all XapiXoType here */ + + getObjects: (opts?: { filter?: (obj: XapiXoObject) => boolean }) => Record + getObject: (id: T['id'], type: T['type']) => T + + // how to return the right type for getXapiObject? + getXapiObject: (objOrId: XapiXoObject['id'] | XapiXoObject, type: XapiXoObject['type']) => T + + getAllXenServers: () => Promise + getXenServer: (id: XoServer['id']) => Promise + + addSseClient: (id: symbol, client: Response) => void + removeSseClient: (id: symbol) => void +} diff --git a/@xen-orchestra/rest-api/tsconfig.json b/@xen-orchestra/rest-api/tsconfig.json new file mode 100644 index 00000000000..94463554305 --- /dev/null +++ b/@xen-orchestra/rest-api/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + /* Basic Options */ + "incremental": true, + "target": "ESNext", + "module": "NodeNext", + "outDir": "./dist", + "rootDir": "./src", + "allowJs": true, + + /* Strict Type-Checking Options */ + "strict": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "noImplicitAny": false, + "alwaysStrict": true, + + /* Additional Checks */ + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + + /* Module Resolution Options */ + "baseUrl": "./", + "esModuleInterop": true, + + /* Experimental Options */ + "experimentalDecorators": true, + + /* Advanced Options */ + "moduleResolution": "nodenext", + "resolveJsonModule": true + }, + "include": ["./src/**/*"] +} diff --git a/@xen-orchestra/rest-api/tsoa.json b/@xen-orchestra/rest-api/tsoa.json new file mode 100644 index 00000000000..d1372a2d680 --- /dev/null +++ b/@xen-orchestra/rest-api/tsoa.json @@ -0,0 +1,49 @@ +{ + "entryFile": "./src/index.ts", + "noImplicitAdditionalProperties": "throw-on-extras", + "controllerPathGlobs": ["./src/**/*.controller.ts"], + "spec": { + "outputDirectory": "./src/open-api/spec", + "specVersion": 3, + "basePath": "/rest/v1", + "securityDefinitions": { + "basic_auth": { + "type": "http", + "scheme": "basic" + }, + "token": { + "type": "apiKey", + "name": "authenticationToken", + "in": "cookie" + } + }, + "specMerging": "recursive", + "spec": { + "paths": { + "/srs/{id}/actions/import_vdi": { + "post": { + "requestBody": { + "required": true, + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + } + } + }, + "routes": { + "routesDir": "./src/open-api/routes", + "esm": true, + "middleware": "express", + "basePath": "/rest/v1", + "iocModule": "./src/ioc/ioc.ts", + "authenticationModule": "./src/middleware/authentication.ts" + } +} diff --git a/packages/xo-collection/src/collection.js b/packages/xo-collection/src/collection.js index 2ec2f45f0b3..cc97af0508a 100644 --- a/packages/xo-collection/src/collection.js +++ b/packages/xo-collection/src/collection.js @@ -110,6 +110,10 @@ export class Collection extends EventEmitter { return this._size } + get allIndexes() { + return this._indexes + } + // ----------------------------------------------------------------- // Manipulation // ----------------------------------------------------------------- diff --git a/packages/xo-collection/src/index.js b/packages/xo-collection/src/index.js index 3769a327f50..c8cfcf769ab 100644 --- a/packages/xo-collection/src/index.js +++ b/packages/xo-collection/src/index.js @@ -4,11 +4,12 @@ import clearObject from './clear-object' import isEmpty from './is-empty' import NotImplemented from './not-implemented' import { ACTION_ADD, ACTION_UPDATE, ACTION_REMOVE } from './collection' - +import { EventEmitter } from 'node:events' // =================================================================== -export class Index { +export class Index extends EventEmitter { constructor(computeHash) { + super() if (computeHash) { this.computeHash = iteratee(computeHash) } @@ -85,6 +86,7 @@ export class Index { (itemsByHash[hash] = {}))[key] = value keysToHash[key] = hash + this.emit('add', hash, value) } } } @@ -108,6 +110,7 @@ export class Index { (itemsByHash[hash] = {}))[key] = value keysToHash[key] = hash + this.emit('update', hash, value) } else { delete keysToHash[key] } @@ -120,6 +123,7 @@ export class Index { for (const key in items) { const prev = keysToHash[key] if (prev != null) { + this.emit('remove', prev, itemsByHash[prev][key]) delete itemsByHash[prev][key] delete keysToHash[key] } diff --git a/packages/xo-server/package.json b/packages/xo-server/package.json index f8ce5617d1b..bdb7ac355a9 100644 --- a/packages/xo-server/package.json +++ b/packages/xo-server/package.json @@ -54,6 +54,7 @@ "@xen-orchestra/log": "^0.7.1", "@xen-orchestra/mixin": "^0.2.0", "@xen-orchestra/mixins": "^0.16.2", + "@xen-orchestra/rest-api": "^0.1.0", "@xen-orchestra/self-signed": "^0.2.1", "@xen-orchestra/template": "^0.1.0", "@xen-orchestra/vmware-explorer": "^0.8.5", diff --git a/packages/xo-server/src/index.mjs b/packages/xo-server/src/index.mjs index dbfb8695e76..a559720f379 100644 --- a/packages/xo-server/src/index.mjs +++ b/packages/xo-server/src/index.mjs @@ -52,6 +52,7 @@ import transportConsole from '@xen-orchestra/log/transports/console' import { configure } from '@xen-orchestra/log/configure' import { generateToken } from './utils.mjs' import { ProxyAgent } from 'proxy-agent' +import { getRestApi } from '@xen-orchestra/rest-api' // =================================================================== @@ -117,7 +118,11 @@ async function createExpressApp(config) { app.use(helmet(config.http.helmet)) - app.use(compression()) + app.use( + compression({ + filter: req => req.url !== '/rest/v1/events', // compression not compatible with EventSource?? it supposed to work with eventsource@3 + }) + ) let { sessionSecret } = config.http if (sessionSecret === undefined) { @@ -920,6 +925,51 @@ export default async function main(args) { if (!safeMode) { await registerPlugins(xo) + // test to register a route on the fly + await getRestApi().registerRoute( + 'netbox/sync', + (req, res) => { + res.send('route registered') + }, + { + get: { + operationId: 'getSyncNetbox', + responses: { + 200: { + description: 'Ok', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/Dashboard', + }, + }, + }, + }, + }, + security: [], + parameters: [], + }, + } + ) + + await getRestApi().registerRoute( + '/foo/bar', + (req, res) => { + res.send('fo bar') + }, + { + get: { + operationId: 'getFoobar', + responses: { + 200: { + description: 'Ok', + }, + }, + security: [], + parameters: [], + }, + } + ) xo.emit('plugins:registered') } diff --git a/packages/xo-server/src/xo-mixins/rest-api-poc.mjs b/packages/xo-server/src/xo-mixins/rest-api-poc.mjs new file mode 100644 index 00000000000..d27a1fac897 --- /dev/null +++ b/packages/xo-server/src/xo-mixins/rest-api-poc.mjs @@ -0,0 +1,11 @@ +import setupRestApi from '@xen-orchestra/rest-api' + +export default class ClassRestApiPoc { + constructor(app, { express }) { + if (express === undefined) { + return + } + + setupRestApi(express, app) + } +} diff --git a/yarn.lock b/yarn.lock index 5d7994a89b4..53214c63560 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2006,7 +2006,7 @@ dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== @@ -2016,6 +2016,22 @@ resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.2.5.tgz#438f8bbe105341853469b2cf2d10b6321cadeb3a" integrity sha512-5iuG/StT+7OfvhoBHPlmxkPA9om6aDUFgmD4+mWKAGsYt4vCe8rypneG03AuseyRHBmcCLXQtIH5S26tIoggLg== +"@eslint/config-array@^0.19.0": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.1.tgz#734aaea2c40be22bbb1f2a9dac687c57a6a4c984" + integrity sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA== + dependencies: + "@eslint/object-schema" "^2.1.5" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" + integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== + dependencies: + "@types/json-schema" "^7.0.15" + "@eslint/eslintrc@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" @@ -2031,7 +2047,7 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^3.0.0": +"@eslint/eslintrc@^3.0.0", "@eslint/eslintrc@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== @@ -2051,6 +2067,24 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@eslint/js@9.18.0": + version "9.18.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.18.0.tgz#3356f85d18ed3627ab107790b53caf7e1e3d1e84" + integrity sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA== + +"@eslint/object-schema@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.5.tgz#8670a8f6258a2be5b2c620ff314a1d984c23eb2e" + integrity sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ== + +"@eslint/plugin-kit@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" + integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== + dependencies: + "@eslint/core" "^0.10.0" + levn "^0.4.1" + "@floating-ui/core@^1.6.0": version "1.6.9" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.9.tgz#64d1da251433019dafa091de9b2886ff35ec14e6" @@ -2135,6 +2169,269 @@ normalize-path "^2.0.1" through2 "^2.0.3" +"@hapi/accept@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-6.0.3.tgz#eef0800a4f89cd969da8e5d0311dc877c37279ab" + integrity sha512-p72f9k56EuF0n3MwlBNThyVE5PXX40g+aQh+C/xbKrfzahM2Oispv3AXmOIU51t3j77zay1qrX7IIziZXspMlw== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/ammo@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@hapi/ammo/-/ammo-6.0.1.tgz#1bc9f7102724ff288ca03b721854fc5393ad123a" + integrity sha512-pmL+nPod4g58kXrMcsGLp05O2jF4P2Q3GiL8qYV7nKYEh3cGf+rV4P5Jyi2Uq0agGhVU63GtaSAfBEZOlrJn9w== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/b64@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@hapi/b64/-/b64-6.0.1.tgz#786b47dc070e14465af49e2428c1025bd06ed3df" + integrity sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/boom@^10.0.0", "@hapi/boom@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-10.0.1.tgz#ebb14688275ae150aa6af788dbe482e6a6062685" + integrity sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/bounce@^3.0.1", "@hapi/bounce@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@hapi/bounce/-/bounce-3.0.2.tgz#6499a1e2db6a699e2c8d284045af46b9d8d9d57d" + integrity sha512-d0XmlTi3H9HFDHhQLjg4F4auL1EY3Wqj7j7/hGDhFFe6xAbnm3qiGrXeT93zZnPH8gH+SKAFYiRzu26xkXcH3g== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/bourne@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-3.0.0.tgz#f11fdf7dda62fe8e336fa7c6642d9041f30356d7" + integrity sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w== + +"@hapi/call@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@hapi/call/-/call-9.0.1.tgz#569b87d5b67abf0e58fb82a3894a61aaed3ca92e" + integrity sha512-uPojQRqEL1GRZR4xXPqcLMujQGaEpyVPRyBlD8Pp5rqgIwLhtveF9PkixiKru2THXvuN8mUrLeet5fqxKAAMGg== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/catbox-memory@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@hapi/catbox-memory/-/catbox-memory-6.0.2.tgz#399fa83e85134d45a548eee978e4c3c1523e1a70" + integrity sha512-H1l4ugoFW/ZRkqeFrIo8p1rWN0PA4MDTfu4JmcoNDvnY975o29mqoZblqFTotxNHlEkMPpIiIBJTV+Mbi+aF0g== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/catbox@^12.1.1": + version "12.1.1" + resolved "https://registry.yarnpkg.com/@hapi/catbox/-/catbox-12.1.1.tgz#9339dca0a5b18b3ca0a825ac5dfc916dbc5bab83" + integrity sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + "@hapi/podium" "^5.0.0" + "@hapi/validate" "^2.0.1" + +"@hapi/content@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@hapi/content/-/content-6.0.0.tgz#2427af3bac8a2f743512fce2a70cbdc365af29df" + integrity sha512-CEhs7j+H0iQffKfe5Htdak5LBOz/Qc8TRh51cF+BFv0qnuph3Em4pjGVzJMkI2gfTDdlJKWJISGWS1rK34POGA== + dependencies: + "@hapi/boom" "^10.0.0" + +"@hapi/cryptiles@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@hapi/cryptiles/-/cryptiles-6.0.1.tgz#7868a9d4233567ed66f0a9caf85fdcc56e980621" + integrity sha512-9GM9ECEHfR8lk5ASOKG4+4ZsEzFqLfhiryIJ2ISePVB92OHLp/yne4m+zn7z9dgvM98TLpiFebjDFQ0UHcqxXQ== + dependencies: + "@hapi/boom" "^10.0.1" + +"@hapi/file@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@hapi/file/-/file-3.0.0.tgz#f1fd824493ac89a6fceaf89c824afc5ae2121c09" + integrity sha512-w+lKW+yRrLhJu620jT3y+5g2mHqnKfepreykvdOcl9/6up8GrQQn+l3FRTsjHTKbkbfQFkuksHpdv2EcpKcJ4Q== + +"@hapi/hapi@^21.3.12": + version "21.3.12" + resolved "https://registry.yarnpkg.com/@hapi/hapi/-/hapi-21.3.12.tgz#a9c48869199d15c22e53d9c22153c9323919e5a0" + integrity sha512-GCUP12dkb3QMjpFl+wEFO73nqKRmsnD5um/QDOn6lj2GjGBrDXPcT194mNARO+PPNXZOR4KmvIpHt/lceUncfg== + dependencies: + "@hapi/accept" "^6.0.3" + "@hapi/ammo" "^6.0.1" + "@hapi/boom" "^10.0.1" + "@hapi/bounce" "^3.0.2" + "@hapi/call" "^9.0.1" + "@hapi/catbox" "^12.1.1" + "@hapi/catbox-memory" "^6.0.2" + "@hapi/heavy" "^8.0.1" + "@hapi/hoek" "^11.0.6" + "@hapi/mimos" "^7.0.1" + "@hapi/podium" "^5.0.1" + "@hapi/shot" "^6.0.1" + "@hapi/somever" "^4.1.1" + "@hapi/statehood" "^8.1.1" + "@hapi/subtext" "^8.1.0" + "@hapi/teamwork" "^6.0.0" + "@hapi/topo" "^6.0.2" + "@hapi/validate" "^2.0.1" + +"@hapi/heavy@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@hapi/heavy/-/heavy-8.0.1.tgz#e2be4a6a249005b5a587f7604aafa8ed02461fb6" + integrity sha512-gBD/NANosNCOp6RsYTsjo2vhr5eYA3BEuogk6cxY0QdhllkkTaJFYtTXv46xd6qhBVMbMMqcSdtqey+UQU3//w== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hoek" "^11.0.2" + "@hapi/validate" "^2.0.1" + +"@hapi/hoek@^11.0.2", "@hapi/hoek@^11.0.6": + version "11.0.7" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.7.tgz#56a920793e0a42d10e530da9a64cc0d3919c4002" + integrity sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ== + +"@hapi/iron@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@hapi/iron/-/iron-7.0.1.tgz#f74bace8dad9340c7c012c27c078504f070f14b5" + integrity sha512-tEZnrOujKpS6jLKliyWBl3A9PaE+ppuL/+gkbyPPDb/l2KSKQyH4lhMkVb+sBhwN+qaxxlig01JRqB8dk/mPxQ== + dependencies: + "@hapi/b64" "^6.0.1" + "@hapi/boom" "^10.0.1" + "@hapi/bourne" "^3.0.0" + "@hapi/cryptiles" "^6.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/mimos@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@hapi/mimos/-/mimos-7.0.1.tgz#5b65c76bb9da28ba34b0092215891f2c72bc899d" + integrity sha512-b79V+BrG0gJ9zcRx1VGcCI6r6GEzzZUgiGEJVoq5gwzuB2Ig9Cax8dUuBauQCFKvl2YWSWyOc8mZ8HDaJOtkew== + dependencies: + "@hapi/hoek" "^11.0.2" + mime-db "^1.52.0" + +"@hapi/nigel@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@hapi/nigel/-/nigel-5.0.1.tgz#a6dfe357e9d48d944e2ffc552bd95cb701d79ee9" + integrity sha512-uv3dtYuB4IsNaha+tigWmN8mQw/O9Qzl5U26Gm4ZcJVtDdB1AVJOwX3X5wOX+A07qzpEZnOMBAm8jjSqGsU6Nw== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/vise" "^5.0.1" + +"@hapi/pez@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@hapi/pez/-/pez-6.1.0.tgz#64d9f95580fc7d8f1d13437ee4a8676709954fda" + integrity sha512-+FE3sFPYuXCpuVeHQ/Qag1b45clR2o54QoonE/gKHv9gukxQ8oJJZPR7o3/ydDTK6racnCJXxOyT1T93FCJMIg== + dependencies: + "@hapi/b64" "^6.0.1" + "@hapi/boom" "^10.0.1" + "@hapi/content" "^6.0.0" + "@hapi/hoek" "^11.0.2" + "@hapi/nigel" "^5.0.1" + +"@hapi/podium@^5.0.0", "@hapi/podium@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@hapi/podium/-/podium-5.0.2.tgz#6b1431bec13f55525cdfa83b2a7684c212c96189" + integrity sha512-T7gf2JYHQQfEfewTQFbsaXoZxSvuXO/QBIGljucUQ/lmPnTTNAepoIKOakWNVWvo2fMEDjycu77r8k6dhreqHA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/teamwork" "^6.0.0" + "@hapi/validate" "^2.0.1" + +"@hapi/shot@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@hapi/shot/-/shot-6.0.1.tgz#ea84d1810b7c8599d5517c23b4ec55a529d7dc16" + integrity sha512-s5ynMKZXYoDd3dqPw5YTvOR/vjHvMTxc388+0qL0jZZP1+uwXuUD32o9DuuuLsmTlyXCWi02BJl1pBpwRuUrNA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/validate" "^2.0.1" + +"@hapi/somever@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@hapi/somever/-/somever-4.1.1.tgz#b492c78408303c72cd1a39c5060f35d18a404b27" + integrity sha512-lt3QQiDDOVRatS0ionFDNrDIv4eXz58IibQaZQDOg4DqqdNme8oa0iPWcE0+hkq/KTeBCPtEOjDOBKBKwDumVg== + dependencies: + "@hapi/bounce" "^3.0.1" + "@hapi/hoek" "^11.0.2" + +"@hapi/statehood@^8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@hapi/statehood/-/statehood-8.1.1.tgz#db4bd14c90810a1389763cb0b0b8f221aa4179c1" + integrity sha512-YbK7PSVUA59NArAW5Np0tKRoIZ5VNYUicOk7uJmWZF6XyH5gGL+k62w77SIJb0AoAJ0QdGQMCQ/WOGL1S3Ydow== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/bounce" "^3.0.1" + "@hapi/bourne" "^3.0.0" + "@hapi/cryptiles" "^6.0.1" + "@hapi/hoek" "^11.0.2" + "@hapi/iron" "^7.0.1" + "@hapi/validate" "^2.0.1" + +"@hapi/subtext@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@hapi/subtext/-/subtext-8.1.0.tgz#58733020a6655bc4d978df9e2f75e31696ff3f91" + integrity sha512-PyaN4oSMtqPjjVxLny1k0iYg4+fwGusIhaom9B2StinBclHs7v46mIW706Y+Wo21lcgulGyXbQrmT/w4dus6ww== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/bourne" "^3.0.0" + "@hapi/content" "^6.0.0" + "@hapi/file" "^3.0.0" + "@hapi/hoek" "^11.0.2" + "@hapi/pez" "^6.1.0" + "@hapi/wreck" "^18.0.1" + +"@hapi/teamwork@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-6.0.0.tgz#b3a173cf811ba59fc6ee22318a1b51f4561f06e0" + integrity sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A== + +"@hapi/topo@^6.0.1", "@hapi/topo@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-6.0.2.tgz#f219c1c60da8430228af4c1f2e40c32a0d84bbb4" + integrity sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/validate@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@hapi/validate/-/validate-2.0.1.tgz#45cf228c4c8cfc61ba2da7e0a5ba93ff3b9afff1" + integrity sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/topo" "^6.0.1" + +"@hapi/vise@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@hapi/vise/-/vise-5.0.1.tgz#5c9f16bcf1c039ddd4b6cad5f32d71eeb6bb7dac" + integrity sha512-XZYWzzRtINQLedPYlIkSkUr7m5Ddwlu99V9elh8CSygXstfv3UnWIXT0QD+wmR0VAG34d2Vx3olqcEhRRoTu9A== + dependencies: + "@hapi/hoek" "^11.0.2" + +"@hapi/wreck@^18.0.1": + version "18.1.0" + resolved "https://registry.yarnpkg.com/@hapi/wreck/-/wreck-18.1.0.tgz#68e631fc7568ebefc6252d5b86cb804466c8dbe6" + integrity sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/bourne" "^3.0.0" + "@hapi/hoek" "^11.0.2" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" @@ -2154,6 +2451,16 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + "@iarna/toml@^2.2.0", "@iarna/toml@^2.2.1", "@iarna/toml@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -2239,6 +2546,24 @@ source-map-js "^1.0.2" unplugin "^1.1.0" +"@inversifyjs/common@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@inversifyjs/common/-/common-1.4.0.tgz#4df42e8cb012a1630ebf2f3c65bb76ac5b0f3e4c" + integrity sha512-qfRJ/3iOlCL/VfJq8+4o5X4oA14cZSBbpAmHsYj8EsIit1xDndoOl0xKOyglKtQD4u4gdNVxMHx4RWARk/I4QA== + +"@inversifyjs/core@1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@inversifyjs/core/-/core-1.3.5.tgz#c02ee3ed036aae40189302794f16a9f4e0ed4557" + integrity sha512-B4MFXabhNTAmrfgB+yeD6wd/GIvmvWC6IQ8Rh/j2C3Ix69kmqwz9pr8Jt3E+Nho9aEHOQCZaGmrALgtqRd+oEQ== + dependencies: + "@inversifyjs/common" "1.4.0" + "@inversifyjs/reflect-metadata-utils" "0.2.4" + +"@inversifyjs/reflect-metadata-utils@0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@inversifyjs/reflect-metadata-utils/-/reflect-metadata-utils-0.2.4.tgz#c65172283db9516c4a27e8d673ca7a31a07d528b" + integrity sha512-u95rV3lKfG+NT2Uy/5vNzoDujos8vN8O18SSA5UyhxsGYd4GLQn/eUsGXfOsfa7m34eKrDelTKRUX1m/BcNX5w== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -3080,6 +3405,11 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@scarf/scarf@=1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972" + integrity sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ== + "@sigstore/bundle@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-2.3.2.tgz#ad4dbb95d665405fd4a7a02c8a073dbd01e4e95e" @@ -4013,6 +4343,37 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node20/-/node20-20.1.4.tgz#3457d42eddf12d3bde3976186ab0cd22b85df928" integrity sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg== +"@tsoa/cli@^6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@tsoa/cli/-/cli-6.6.0.tgz#7946adfb64a3f081d18d1b4b8e4546626b99c602" + integrity sha512-thSW0EiqjkF7HspcPIVIy0ZX65VqbWALHbxwl8Sk83j2kakOMq+fJvfo8FcBAWlMki+JDH7CO5iaAaSLHbeqtg== + dependencies: + "@tsoa/runtime" "^6.6.0" + "@types/multer" "^1.4.12" + fs-extra "^11.2.0" + glob "^10.3.10" + handlebars "^4.7.8" + merge-anything "^5.1.7" + minimatch "^9.0.1" + ts-deepmerge "^7.0.2" + typescript "^5.7.2" + validator "^13.12.0" + yaml "^2.6.1" + yargs "^17.7.1" + +"@tsoa/runtime@^6.6.0": + version "6.6.0" + resolved "https://registry.yarnpkg.com/@tsoa/runtime/-/runtime-6.6.0.tgz#8cbe2773fed7867979b6dee51d6c8abd5d264e14" + integrity sha512-+rF2gdL8CX+jQ82/IBc+MRJFNAvWPoBBl77HHJv3ESVMqbKhlhlo97JHmKyFbLcX6XOJN8zl8gfQpAEJN4SOMQ== + dependencies: + "@hapi/boom" "^10.0.1" + "@hapi/hapi" "^21.3.12" + "@types/koa" "^2.15.0" + "@types/multer" "^1.4.12" + express "^4.21.2" + reflect-metadata "^0.2.2" + validator "^13.12.0" + "@tufjs/canonical-json@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz#a52f61a3d7374833fca945b2549bc30a2dd40d0a" @@ -4026,6 +4387,13 @@ "@tufjs/canonical-json" "2.0.0" minimatch "^9.0.4" +"@types/accepts@*": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" + integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== + dependencies: + "@types/node" "*" + "@types/asn1@>=0.2.1": version "0.2.4" resolved "https://registry.yarnpkg.com/@types/asn1/-/asn1-0.2.4.tgz#a0f89f9ddad8186c9c081c5df2e5cade855d2ac0" @@ -4078,6 +4446,14 @@ dependencies: "@types/babel-types" "*" +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/cheerio@^0.22.22": version "0.22.35" resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.35.tgz#0d16dc1f24d426231c181b9c31847f673867595f" @@ -4085,6 +4461,18 @@ dependencies: "@types/node" "*" +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" + integrity sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg== + "@types/conventional-commits-parser@^5.0.0": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz#8cb81cf170853496cbc501a3b32dcf5e46ffb61a" @@ -4097,16 +4485,46 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== +"@types/cookies@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.9.0.tgz#a2290cfb325f75f0f28720939bee854d4142aee2" + integrity sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + "@types/d3-time-format@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2" integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg== -"@types/estree@1.0.6", "@types/estree@^1.0.0": +"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": 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@^5.0.0": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.5.tgz#f6a851c7fd512e5da087f6f20d29f44b162a6a95" + integrity sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" + integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/file-saver@^2.0.7": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.7.tgz#8dbb2f24bdc7486c54aa854eb414940bbd056f7d" @@ -4127,6 +4545,16 @@ dependencies: "@types/node" "*" +"@types/http-assert@*": + version "1.5.6" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.6.tgz#b6b657c38a2350d21ce213139f33b03b2b5fa431" + integrity sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw== + +"@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" @@ -4146,7 +4574,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@^7.0.12": +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -4163,6 +4591,32 @@ dependencies: "@types/node" "*" +"@types/keygrip@*": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" + integrity sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ== + +"@types/koa-compose@*": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.8.tgz#dec48de1f6b3d87f87320097686a915f1e954b57" + integrity sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.15.0": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.15.0.tgz#eca43d76f527c803b491731f95df575636e7b6f2" + integrity sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + "@types/lodash-es@^4.17.12": version "4.17.12" resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" @@ -4175,11 +4629,23 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.14.tgz#bafc053533f4cdc5fcc9635af46a963c1f3deaff" integrity sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A== +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + "@types/minimist@^1.2.2": version "1.2.5" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== +"@types/multer@^1.4.12": + version "1.4.12" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.12.tgz#da67bd0c809f3a63fe097c458c0d4af1fea50ab7" + integrity sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg== + dependencies: + "@types/express" "*" + "@types/node@*", "@types/node@>=16", "@types/node@>=18.0.0": version "22.10.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.6.tgz#5c6795e71635876039f853cbccd59f523d9e4239" @@ -4201,6 +4667,13 @@ dependencies: undici-types "~6.19.2" +"@types/node@^22.10.6": + version "22.10.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.10.tgz#85fe89f8bf459dc57dfef1689bd5b52ad1af07e6" + integrity sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww== + dependencies: + undici-types "~6.20.0" + "@types/novnc__novnc@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@types/novnc__novnc/-/novnc__novnc-1.5.0.tgz#5f58687f0fa6591b75e91eb75237520da440dbb4" @@ -4216,16 +4689,51 @@ resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.10.tgz#52f8dbd6113517aef901db20b4f3fca543b88c1f" integrity sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA== +"@types/qs@*": + version "6.9.18" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2" + integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + "@types/semver@^7.5.0": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@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/swagger-ui-express@^4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.7.tgz#a7b6d8cfc6b1c35723e92284507cf230ad22266a" + integrity sha512-ovLM9dNincXkzH4YwyYpll75vhzPBlWx6La89wwvYH7mHjVpf0X0K/vR/aUM7SRxmr5tt9z7E5XJcjQ46q+S3g== + dependencies: + "@types/express" "*" + "@types/serve-static" "*" + "@types/through2@^2.0.31": version "2.0.41" resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.41.tgz#3e5e1720d71ffdfa03c22f2aad6593d12a47034f" @@ -4292,6 +4800,21 @@ natural-compare "^1.4.0" ts-api-utils "^1.3.0" +"@typescript-eslint/eslint-plugin@^8.20.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz#395014a75112ecdb81142b866ab6bb62e3be0f2a" + integrity sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.21.0" + "@typescript-eslint/type-utils" "8.21.0" + "@typescript-eslint/utils" "8.21.0" + "@typescript-eslint/visitor-keys" "8.21.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.0" + "@typescript-eslint/parser@^6.7.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" @@ -4314,6 +4837,17 @@ "@typescript-eslint/visitor-keys" "7.18.0" debug "^4.3.4" +"@typescript-eslint/parser@^8.20.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.21.0.tgz#312c638aaba4f640d45bfde7c6795a9d75deb088" + integrity sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA== + dependencies: + "@typescript-eslint/scope-manager" "8.21.0" + "@typescript-eslint/types" "8.21.0" + "@typescript-eslint/typescript-estree" "8.21.0" + "@typescript-eslint/visitor-keys" "8.21.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" @@ -4330,6 +4864,14 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/visitor-keys" "7.18.0" +"@typescript-eslint/scope-manager@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz#d08d94e2a34b4ccdcc975543c25bb62917437500" + integrity sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA== + dependencies: + "@typescript-eslint/types" "8.21.0" + "@typescript-eslint/visitor-keys" "8.21.0" + "@typescript-eslint/type-utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" @@ -4350,6 +4892,16 @@ debug "^4.3.4" ts-api-utils "^1.3.0" +"@typescript-eslint/type-utils@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz#2e69d1a93cdbedc73fe694cd6ae4dfedd00430a0" + integrity sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ== + dependencies: + "@typescript-eslint/typescript-estree" "8.21.0" + "@typescript-eslint/utils" "8.21.0" + debug "^4.3.4" + ts-api-utils "^2.0.0" + "@typescript-eslint/types@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" @@ -4360,6 +4912,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== +"@typescript-eslint/types@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.21.0.tgz#58f30aec8db8212fd886835dc5969cdf47cb29f5" + integrity sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A== + "@typescript-eslint/typescript-estree@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" @@ -4388,6 +4945,20 @@ semver "^7.6.0" ts-api-utils "^1.3.0" +"@typescript-eslint/typescript-estree@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz#5ce71acdbed3b97b959f6168afba5a03c88f69a9" + integrity sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg== + dependencies: + "@typescript-eslint/types" "8.21.0" + "@typescript-eslint/visitor-keys" "8.21.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.0" + "@typescript-eslint/utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" @@ -4411,6 +4982,16 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/typescript-estree" "7.18.0" +"@typescript-eslint/utils@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.21.0.tgz#bc4874fbc30feb3298b926e3b03d94570b3999c5" + integrity sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.21.0" + "@typescript-eslint/types" "8.21.0" + "@typescript-eslint/typescript-estree" "8.21.0" + "@typescript-eslint/visitor-keys@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" @@ -4427,6 +5008,14 @@ "@typescript-eslint/types" "7.18.0" eslint-visitor-keys "^3.4.3" +"@typescript-eslint/visitor-keys@8.21.0": + version "8.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz#a89744c4cdc83b5c761eb5878befe6c33d1481b2" + integrity sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w== + dependencies: + "@typescript-eslint/types" "8.21.0" + eslint-visitor-keys "^4.2.0" + "@ungap/structured-clone@^1.2.0": version "1.2.1" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd" @@ -7418,7 +8007,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -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, cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -9075,6 +9664,14 @@ eslint-scope@^7.1.1, eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -9134,6 +9731,46 @@ eslint@^8.7.0: strip-ansi "^6.0.1" text-table "^0.2.0" +eslint@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.18.0.tgz#c95b24de1183e865de19f607fda6518b54827850" + integrity sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.10.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.18.0" + "@eslint/plugin-kit" "^0.2.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + esm@^3.0.84: version "3.2.25" resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" @@ -9149,7 +9786,7 @@ esniff@^2.0.1: event-emitter "^0.3.5" type "^2.7.2" -espree@^10.0.1: +espree@^10.0.1, espree@^10.3.0: version "10.3.0" resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== @@ -9172,7 +9809,7 @@ esprima@^4.0.0, esprima@^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.0, esquery@^1.4.2: +esquery@^1.4.0, esquery@^1.4.2, esquery@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== @@ -9449,7 +10086,7 @@ express-session@^1.15.6: safe-buffer "5.2.1" uid-safe "~2.1.5" -express@^4.16.2: +express@^4.16.2, express@^4.21.2: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== @@ -9708,6 +10345,13 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-saver@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38" @@ -9877,6 +10521,14 @@ flat-cache@^3.0.4: keyv "^4.5.3" rimraf "^3.0.2" +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flatted@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" @@ -10017,6 +10669,15 @@ fs-extra@^11.1.0, fs-extra@^11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d" + integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -11431,6 +12092,19 @@ invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.1.2, invarian dependencies: loose-envify "^1.0.0" +inversify-binding-decorators@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/inversify-binding-decorators/-/inversify-binding-decorators-4.0.0.tgz#c4d1ac50d7d6531f465ee7894e92031f7471c865" + integrity sha512-r8au/oH3vS7ttHj0RivAznwElySeRohLfg8lvOSzbrX6abf/8ik8ptk49XbzdShgrnalvl7CM6MjcskfM7MMqQ== + +inversify@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/inversify/-/inversify-6.2.1.tgz#cceafa8d2c648a20713cca64ee8d8f38c9a29d6b" + integrity sha512-W6Xi0icXIiC48RWdT681+GlZVgAKmCrNTiP7hj4IVPFbcxHz+Jj8Gxz5qr/Az2cgcZMYdB8tKIr2e68LUi1LYQ== + dependencies: + "@inversifyjs/common" "1.4.0" + "@inversifyjs/core" "1.3.5" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -11971,6 +12645,11 @@ is-weakset@^2.0.3: call-bound "^1.0.3" get-intrinsic "^1.2.6" +is-what@^4.1.8: + version "4.1.16" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.16.tgz#1ad860a19da8b4895ad5495da3182ce2acdd7a6f" + integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A== + is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -12735,7 +13414,7 @@ keygrip@~1.1.0: dependencies: tsscmp "1.0.6" -keyv@^4.5.3: +keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -13641,6 +14320,13 @@ meow@^12.0.1: resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6" integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== +merge-anything@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/merge-anything/-/merge-anything-5.1.7.tgz#94f364d2b0cf21ac76067b5120e429353b3525d7" + integrity sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ== + dependencies: + is-what "^4.1.8" + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -13720,7 +14406,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@^1.52.0: version "1.53.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== @@ -13807,7 +14493,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0, minimatch@^9.0.3, minimatch@^9.0.4: +minimatch@^9.0.0, minimatch@^9.0.1, minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -18647,6 +19333,20 @@ sver-compat@^1.5.0: es6-iterator "^2.0.1" es6-symbol "^3.1.1" +swagger-ui-dist@>=5.0.0: + version "5.18.2" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz#62013074374d272c04ed3030704b88db5aa8c0b7" + integrity sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw== + dependencies: + "@scarf/scarf" "=1.4.0" + +swagger-ui-express@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz#fb8c1b781d2793a6bd2f8a205a3f4bd6fa020dd8" + integrity sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA== + dependencies: + swagger-ui-dist ">=5.0.0" + symbol-observable@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -18995,6 +19695,16 @@ ts-api-utils@^1.0.1, ts-api-utils@^1.3.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== +ts-api-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.0.tgz#b9d7d5f7ec9f736f4d0f09758b8607979044a900" + integrity sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ== + +ts-deepmerge@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/ts-deepmerge/-/ts-deepmerge-7.0.2.tgz#6333adcde83e4c42366e9a9a7f955c74ee913547" + integrity sha512-akcpDTPuez4xzULo5NwuoKwYRtjQJ9eoNfBACiBMaXwNAx7B1PKfe5wqUFJuW5uKzQ68YjDFwPaWHDG1KnFGsA== + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -19046,6 +19756,14 @@ tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6 resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +tsoa@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/tsoa/-/tsoa-6.6.0.tgz#6a4b1b1227866b44c2884c5fb9ca3dd945c7ddb2" + integrity sha512-7FudRojmbEpbSQ3t1pyG5EjV3scF7/X75giQt1q+tnuGjjJppB8BOEmIdCK/G8S5Dqnmpwz5Q3vxluKozpIW9A== + dependencies: + "@tsoa/cli" "^6.6.0" + "@tsoa/runtime" "^6.6.0" + tsscmp@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" @@ -19214,7 +19932,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@5, typescript@^5.0.3: +typescript@5, typescript@^5.0.3, typescript@^5.7.2: version "5.7.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== @@ -19622,6 +20340,11 @@ validate-npm-package-name@^5.0.0: resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== +validator@^13.12.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" @@ -20377,7 +21100,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.0.0, yargs@^17.3.1, yargs@^17.7.2: +yargs@^17.0.0, yargs@^17.3.1, yargs@^17.7.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==