diff --git a/package.json b/package.json index cd877c7..20667d2 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,11 @@ "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@nestjs/typeorm": "^11.0.0", + "axios": "^1.12.2", "isolated-vm": "^6.0.1", "mariadb": "^3.4.5", "mysql": "^2.18.1", + "mysql2": "^3.15.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "squel": "^5.13.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 45df325..fad6007 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -3,6 +3,8 @@ import { TypeOrmModule } from "@nestjs/typeorm"; import { ConfigModule } from "@nestjs/config"; import { TestModule } from "../test/test.module"; import { ApiModule } from "../api/api.module"; +import { QueryModule } from "src/query/query.module"; +import { ProjectModule } from "src/project/project.module"; @Module({ imports: [ @@ -24,6 +26,8 @@ import { ApiModule } from "../api/api.module"; autoLoadEntities: true, }), ApiModule, + ProjectModule, + QueryModule, TestModule, ], controllers: [], diff --git a/src/migrations/1758401762034-ModulesAndPluigns.ts b/src/migrations/1758401762034-ModulesAndPluigns.ts new file mode 100644 index 0000000..1b6d651 --- /dev/null +++ b/src/migrations/1758401762034-ModulesAndPluigns.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class ModulesAndPluigns1758401762034 implements MigrationInterface { + name = 'ModulesAndPluigns1758401762034' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`plugin\` (\`id\` varchar(36) NOT NULL, \`class\` varchar(255) NOT NULL, \`name\` varchar(255) NOT NULL, \`config\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`plugin\``); + } + +} diff --git a/src/project/project.module.ts b/src/project/project.module.ts index f5088d3..4b08204 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -2,10 +2,12 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { Project } from "./entities/project.entity"; import { ProjectService } from "./project.service"; +import { ProjectController } from "./project.controller"; @Module({ imports: [TypeOrmModule.forFeature([Project])], - controllers: [], + controllers: [ProjectController], providers: [ProjectService], + exports: [ProjectService], }) export class ProjectModule {} diff --git a/src/project/project.service.ts b/src/project/project.service.ts index d442afe..ab72992 100644 --- a/src/project/project.service.ts +++ b/src/project/project.service.ts @@ -14,4 +14,8 @@ export class ProjectService { const project = this.projectRepository.create({ name }); return this.projectRepository.save(project); } + + findByToken(token: string) { + return this.projectRepository.findOne({ where: { token } }); + } } diff --git a/src/query/entities/plugin.entity.ts b/src/query/entities/plugin.entity.ts new file mode 100644 index 0000000..c5a9d8a --- /dev/null +++ b/src/query/entities/plugin.entity.ts @@ -0,0 +1,20 @@ +import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Query } from "./query.entity"; + +@Entity("plugin") +export class VmPlugin { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ type: "varchar", length: 255, nullable: false }) + class: string; + + @Column({ type: "varchar", length: 255, nullable: false }) + name: string; + + @ManyToMany(() => Query, (query) => query.plugins) + queries: Query[]; + + @Column({ type: "varchar", length: 255 }) + config: string; +} diff --git a/src/query/entities/query.entity.ts b/src/query/entities/query.entity.ts index 0f472cd..906909f 100644 --- a/src/query/entities/query.entity.ts +++ b/src/query/entities/query.entity.ts @@ -7,6 +7,7 @@ import { PrimaryGeneratedColumn, } from "typeorm"; import { VMModule } from "./module.entity"; +import { VmPlugin } from "./plugin.entity"; @Entity("query") export class Query { @@ -24,4 +25,7 @@ export class Query { @ManyToMany(() => VMModule, (module) => module.queries) modules: VMModule[]; + + @ManyToMany(() => VmPlugin, (plugin) => plugin.queries) + plugins: VmPlugin[]; } diff --git a/src/query/executer/query.executer.controller.ts b/src/query/executer/query.executer.controller.ts index 414e7e0..df96cd2 100644 --- a/src/query/executer/query.executer.controller.ts +++ b/src/query/executer/query.executer.controller.ts @@ -9,7 +9,10 @@ export class QueryExecuterController { ) {} @Post("/run/:token") - async runQuery(@Param("token") token: string, @Body() query: any) { + async runQuery( + @Param("token") token: string, + @Body() query: Record + ) { return this.queryExecuterService.runQuery(token, query); } } diff --git a/src/query/executer/query.executer.service.ts b/src/query/executer/query.executer.service.ts index a904c88..70ccdaa 100644 --- a/src/query/executer/query.executer.service.ts +++ b/src/query/executer/query.executer.service.ts @@ -4,6 +4,7 @@ import { Query } from "../entities/query.entity"; import { Repository } from "typeorm"; import { Vm } from "../../vm/vm.class"; import { VModule } from "../../vm/module.class"; +import { DatabasePlugin } from "src/vm/plugins/database.plugin"; @Injectable() export class QueryExecuterService { @@ -21,18 +22,38 @@ export class QueryExecuterService { throw new Error("Query not found"); } - const vm = this.createVm(query); - const result = await vm.runScript(query.source); + const vm = await this.createVm(query); + const result = await vm.runScript(query.source, queryData); return { message: "Query executed", result, query: queryData }; } - private createVm(query: Query) { - return new Vm({ - memoryLimit: 5, + private async createVm(query: Query) { + if (query.modules === undefined) { + query.modules = []; + } + + if (query.plugins === undefined) { + query.plugins = []; + } + + const vm = new Vm({ + memoryLimit: 128, modules: query.modules.map((module) => { return new VModule(module.name, module.sourcePath); }), + plugins: query.plugins.map((plugin) => { + switch (plugin.class) { + case "DATABASE": { + const config = JSON.parse(plugin.config); + return DatabasePlugin.init(plugin.name, config); + } + default: + throw new Error(`Unknown plugin class: ${plugin.class}`); + } + }), }); + + return await vm.init(); } } diff --git a/src/query/handler/query.handler.controller.ts b/src/query/handler/query.handler.controller.ts index 7c62647..62b12cf 100644 --- a/src/query/handler/query.handler.controller.ts +++ b/src/query/handler/query.handler.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Inject } from "@nestjs/common"; +import { Body, Controller, Inject, Post } from "@nestjs/common"; import { QueryHandlerService } from "./query.handler.service"; @Controller("query") @@ -7,4 +7,47 @@ export class QueryHandlerController { @Inject(QueryHandlerService) private readonly queryHandlerService: QueryHandlerService ) {} + + @Post("create") + async createQuery( + @Body() queryData: { projectToken: string; source: string } + ) { + return this.queryHandlerService.createQuery(queryData); + } + + @Post("update/:id") + async updateQuery( + @Body() updateData: Partial<{ source: string }>, + @Inject("id") id: string + ) { + return this.queryHandlerService.updateQuery(id, updateData); + } + + @Post("module/create") + async createModule(@Body() moduleData: { name: string; sourcePath: string }) { + return this.queryHandlerService.createModule(moduleData); + } + + @Post("plugin/create") + async createPlugin( + @Body() pluginData: { name: string; class: string; config: string } + ) { + return this.queryHandlerService.createPlugin(pluginData); + } + + @Post("module/add") + async addModuleToQuery(@Body() data: { queryId: string; moduleId: string }) { + return this.queryHandlerService.addModuleToQuery( + data.queryId, + data.moduleId + ); + } + + @Post("plugin/add") + async addPluginToQuery(@Body() data: { queryId: string; pluginId: string }) { + return this.queryHandlerService.addPluginToQuery( + data.queryId, + data.pluginId + ); + } } diff --git a/src/query/handler/query.handler.service.ts b/src/query/handler/query.handler.service.ts index 3c99d3a..b033076 100644 --- a/src/query/handler/query.handler.service.ts +++ b/src/query/handler/query.handler.service.ts @@ -1,4 +1,106 @@ -import { Injectable } from "@nestjs/common"; +import { Inject, Injectable } from "@nestjs/common"; +import { Repository } from "typeorm"; +import { Query } from "../entities/query.entity"; +import { VMModule } from "../entities/module.entity"; +import { VmPlugin } from "../entities/plugin.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { ProjectService } from "src/project/project.service"; @Injectable() -export class QueryHandlerService {} +export class QueryHandlerService { + constructor( + @InjectRepository(Query) + private readonly queryRepository: Repository, + @InjectRepository(VMModule) + private readonly moduleRepository: Repository, + @InjectRepository(VmPlugin) + private readonly pluginRepository: Repository, + @Inject(ProjectService) + private readonly projectService: ProjectService + ) {} + + async createQuery(queryData: { projectToken: string; source: string }) { + const project = await this.projectService.findByToken( + queryData.projectToken + ); + + if (!project) { + throw new Error("Project not found"); + } + + queryData["project"] = project; + delete queryData.projectToken; + + const query = this.queryRepository.create(queryData); + return this.queryRepository.save(query); + } + + async updateQuery(id: string, updateData: Partial) { + const query = await this.queryRepository.findOne({ where: { id } }); + + if (!query) { + throw new Error("Query not found"); + } + + Object.assign(query, updateData); + return this.queryRepository.save(query); + } + + async createModule(moduleData: { name: string; sourcePath: string }) { + const module = this.moduleRepository.create(moduleData); + return this.moduleRepository.save(module); + } + + async createPlugin(pluginData: { + name: string; + class: string; + config: string; + }) { + const plugin = this.pluginRepository.create(pluginData); + return this.pluginRepository.save(plugin); + } + + async addModuleToQuery(queryId: string, moduleId: string) { + const query = await this.queryRepository.findOne({ + where: { id: queryId }, + relations: ["modules"], + }); + + if (!query) { + throw new Error("Query not found"); + } + + const module = await this.moduleRepository.findOne({ + where: { id: moduleId }, + }); + + if (!module) { + throw new Error("Module not found"); + } + + query.modules.push(module); + return this.queryRepository.save(query); + } + + async addPluginToQuery(queryId: string, pluginId: string) { + const query = await this.queryRepository.findOne({ + where: { id: queryId }, + relations: ["plugins"], + }); + + if (!query) { + throw new Error("Query not found"); + } + + const plugin = await this.pluginRepository.findOne({ + where: { id: pluginId }, + }); + + if (!plugin) { + throw new Error("Plugin not found"); + } + + query.plugins.push(plugin); + return this.queryRepository.save(query); + } +} diff --git a/src/query/query.module.ts b/src/query/query.module.ts index c7734d5..a2269f7 100644 --- a/src/query/query.module.ts +++ b/src/query/query.module.ts @@ -1,4 +1,4 @@ -import { Module } from "@nestjs/common"; +import { forwardRef, Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { Query } from "./entities/query.entity"; import { VMModule } from "./entities/module.entity"; @@ -6,9 +6,14 @@ import { QueryExecuterController } from "./executer/query.executer.controller"; import { QueryHandlerController } from "./handler/query.handler.controller"; import { QueryExecuterService } from "./executer/query.executer.service"; import { QueryHandlerService } from "./handler/query.handler.service"; +import { VmPlugin } from "./entities/plugin.entity"; +import { ProjectModule } from "src/project/project.module"; @Module({ - imports: [TypeOrmModule.forFeature([Query, VMModule])], + imports: [ + forwardRef(() => ProjectModule), + TypeOrmModule.forFeature([Query, VMModule, VmPlugin]), + ], controllers: [QueryExecuterController, QueryHandlerController], providers: [QueryExecuterService, QueryHandlerService], }) diff --git a/src/vm/plugin.class.ts b/src/vm/plugin.class.ts new file mode 100644 index 0000000..89dc946 --- /dev/null +++ b/src/vm/plugin.class.ts @@ -0,0 +1,18 @@ +export abstract class Plugin { + protected name: string; + + constructor(name: string) { + this.name = name; + } + + getName(): string { + return this.name; + } + + static init(...args: any[]): any { + return args; + } + + abstract run(...args: any[]): any; + abstract onFinish(...args: any[]): void; +} diff --git a/src/vm/plugins.constants.ts b/src/vm/plugins.constants.ts new file mode 100644 index 0000000..3b9854d --- /dev/null +++ b/src/vm/plugins.constants.ts @@ -0,0 +1,5 @@ +import { DatabasePlugin } from "./plugins/database.plugin"; + +export const PluginClass = { + DATABASE: DatabasePlugin, +}; diff --git a/src/vm/plugins/database.plugin.ts b/src/vm/plugins/database.plugin.ts new file mode 100644 index 0000000..f00f82a --- /dev/null +++ b/src/vm/plugins/database.plugin.ts @@ -0,0 +1,45 @@ +import { Plugin } from "../plugin.class"; +import * as mysql from "mysql2/promise"; + +export class DatabasePlugin extends Plugin { + constructor(name: string, private dbConnection: any) { + if (!dbConnection || typeof dbConnection.execute !== "function") { + throw new Error( + "Invalid database connection: must be a mysql2 connection with execute method" + ); + } + + super(name); + } + + static init( + name: string, + config: { + host: string; + user: string; + password: string; + database: string; + } + ): DatabasePlugin { + const dbConnection = mysql.createConnection({ + host: config.host, + user: config.user, + password: config.password, + database: config.database, + }); + + return new DatabasePlugin(name, dbConnection); + } + + async run(query): Promise<{ + rows: any[]; + fields: any[]; + }> { + const [rows, fields] = await this.dbConnection.execute(query); + return { rows, fields }; + } + + onFinish() { + this.dbConnection.end(); + } +} diff --git a/src/vm/vm.class.ts b/src/vm/vm.class.ts index 26b9de3..c13892f 100644 --- a/src/vm/vm.class.ts +++ b/src/vm/vm.class.ts @@ -1,20 +1,28 @@ import * as ivm from "isolated-vm"; import { VModule } from "./module.class"; +import { Plugin } from "./plugin.class"; export class Vm { private memoryLimit: number; private modules: VModule[]; private context: any; private jail: any; + private plugins: Plugin[]; + private isolate: ivm.Isolate; - constructor(configs: { memoryLimit: number; modules: VModule[] }) { + constructor(configs: { + memoryLimit: number; + modules: VModule[]; + plugins: Plugin[]; + }) { this.memoryLimit = configs.memoryLimit; this.modules = configs.modules; + this.plugins = configs.plugins; } - async init() { - const isolate = new ivm.Isolate({ memoryLimit: this.memoryLimit }); - this.context = isolate.createContext(); + async init(): Promise { + this.isolate = new ivm.Isolate({ memoryLimit: this.memoryLimit }); + this.context = await this.isolate.createContext(); this.jail = this.context.global; this.jail.set("global", this.jail.derefInto()); @@ -22,13 +30,22 @@ export class Vm { for (const mod of this.modules) { this.jail.setSync(mod.getName(), mod.getSource()); } + + for (const plugin of this.plugins) { + this.jail.setSync( + plugin.getName(), + new ivm.Reference(plugin.run.bind(plugin)) + ); + } + + return this; } setFunction(name: string, func: (...args) => any) { this.jail.setSync(name, func); } - async runScript(script: string) { + async runScript(script: string, args: Record): Promise { let resolvePromise: (value: any) => void; let rejectPromise: (reason?: any) => void; @@ -37,11 +54,14 @@ export class Vm { rejectPromise = reject; }); - this.setFunction("result", (...args) => { + this.setFunction("returnResult", (...args) => { console.log("Script result:", args); + resolvePromise(args); }); + // TODO: log + this.setFunction("error", (error: any) => { console.error("Script error:", error); rejectPromise(error); @@ -51,19 +71,25 @@ export class Vm { (async () => { ${script} try { - const result = await main() - result(result) + const result = await main(${JSON.stringify(args)}); + returnResult(result) } catch (e) { error(e) } })() `; - const compiledScript = await this.context.isolate.compileScript( - scriptWithResult - ); + const compiledScript = await this.isolate.compileScript(scriptWithResult); + compiledScript.run(this.context); + this.onFinish(); return await resultPromise; } + + onFinish() { + this.plugins.forEach((plugin) => { + plugin.onFinish(); + }); + } } diff --git a/tests/base/case1.ts b/tests/base/case1.ts new file mode 100644 index 0000000..91290ca --- /dev/null +++ b/tests/base/case1.ts @@ -0,0 +1,22 @@ +import createProject from "../functions/createProject"; +import createQuery from "../functions/createQuery"; +import runQuery from "../functions/runQuery"; + +(async () => { + try { + const project = await createProject("Test Project"); + + const query = await createQuery( + project, + "async function main(input) { return `Hello, ${input.name}!`; }" + ); + + console.log(query); + + const result = await runQuery(query.id, { name: "World" }); + + console.log("Query Result:", result); + } catch (error) { + console.error("Error during test execution:", error); + } +})(); diff --git a/tests/functions/addModule.ts b/tests/functions/addModule.ts new file mode 100644 index 0000000..98670f8 --- /dev/null +++ b/tests/functions/addModule.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +export default async (query: { id: string }, module: { id: string }) => { + try { + const response = await axios.post( + "http://localhost:3000/query/module/add", + { + queryId: query.id, + moduleId: module.id, + } + ); + + return response; + } catch (error) { + console.error("Error in adding module to query:", error); + } +}; diff --git a/tests/functions/addPlugin.ts b/tests/functions/addPlugin.ts new file mode 100644 index 0000000..8b2621e --- /dev/null +++ b/tests/functions/addPlugin.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +export default async (query: { id: string }, plugin: { id: string }) => { + try { + const response = await axios.post( + "http://localhost:3000/query/plugin/add", + { + queryId: query.id, + pluginId: plugin.id, + } + ); + + return response; + } catch (error) { + console.error("Error in adding plugin to query:", error); + } +}; diff --git a/tests/functions/createModule.ts b/tests/functions/createModule.ts new file mode 100644 index 0000000..a2b81e5 --- /dev/null +++ b/tests/functions/createModule.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +export default async (name: string, sourcePath: string) => { + try { + const response = await axios.post( + "http://localhost:3000/query/module/create", + { + name, + sourcePath, + } + ); + + return response; + } catch (error) { + console.error("Error in creating project or query:", error); + } +}; diff --git a/tests/functions/createPluign.ts b/tests/functions/createPluign.ts new file mode 100644 index 0000000..6f13117 --- /dev/null +++ b/tests/functions/createPluign.ts @@ -0,0 +1,18 @@ +import axios from "axios"; + +export default async (name: string, className: string, config: string) => { + try { + const response = await axios.post( + "http://localhost:3000/query/plugin/create", + { + name, + class: className, + config, + } + ); + + return response; + } catch (error) { + console.error("Error in creating project or query:", error); + } +}; diff --git a/tests/functions/createProject.ts b/tests/functions/createProject.ts new file mode 100644 index 0000000..cbdeda1 --- /dev/null +++ b/tests/functions/createProject.ts @@ -0,0 +1,16 @@ +import axios from "axios"; + +const createProject = async (name: string) => { + try { + const response = await axios.put("http://localhost:3000/project/create", { + name: name, + }); + + return response.data; + } catch (error) { + console.error("Error creating project:", error); + throw error; + } +}; + +export default createProject; diff --git a/tests/functions/createQuery.ts b/tests/functions/createQuery.ts new file mode 100644 index 0000000..0d02a12 --- /dev/null +++ b/tests/functions/createQuery.ts @@ -0,0 +1,14 @@ +import axios from "axios"; + +export default async (project: { token: string }, source: string) => { + try { + const response = await axios.post("http://localhost:3000/query/create", { + source, + projectToken: project.token, + }); + return response.data; + } catch (error) { + console.error("Error creating query:", error); + throw error; + } +}; diff --git a/tests/functions/runQuery.ts b/tests/functions/runQuery.ts new file mode 100644 index 0000000..ed62c77 --- /dev/null +++ b/tests/functions/runQuery.ts @@ -0,0 +1,14 @@ +import axios from "axios"; + +export default async (token: string, queryData: Record) => { + try { + const response = await axios.post( + `http://localhost:3000/query/run/${token}`, + queryData + ); + + return response; + } catch (error) { + console.error("Error in running query:", error); + } +}; diff --git a/tests/functions/updateQuery.ts b/tests/functions/updateQuery.ts new file mode 100644 index 0000000..d0c5d59 --- /dev/null +++ b/tests/functions/updateQuery.ts @@ -0,0 +1,14 @@ +import axios from "axios"; + +export default async (query: { id: string; source: string }) => { + try { + const response = await axios.post("http://localhost:3000/query/update", { + queryId: query.id, + source: query.source, + }); + + return response; + } catch (error) { + console.error("Error in updating query:", error); + } +}; diff --git a/yarn.lock b/yarn.lock index 7eba317..4b36ee5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -819,6 +819,11 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -826,6 +831,20 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +aws-ssl-profiles@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641" + integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g== + +axios@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7" + integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.4" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1103,6 +1122,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -1255,6 +1281,11 @@ define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + denque@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" @@ -1394,6 +1425,16 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -1709,6 +1750,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +follow-redirects@^1.15.6: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + for-each@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" @@ -1742,6 +1788,17 @@ fork-ts-checker-webpack-plugin@8.0.0: semver "^7.3.5" tapable "^2.2.1" +form-data@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -1786,12 +1843,19 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -1975,6 +2039,13 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" + integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -2124,6 +2195,11 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -2292,11 +2368,26 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +long@^5.2.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +lru.min@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/lru.min/-/lru.min-1.1.2.tgz#01ce1d72cc50c7faf8bd1f809ebf05d4331021eb" + integrity sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg== + macos-release@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.1.tgz#bccac4a8f7b93163a8d163b8ebf385b3c5f55bf9" @@ -2375,7 +2466,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-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -2473,6 +2564,21 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mysql2@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.15.0.tgz#4cd4a13be2cc48be545b91f57a7e9b6a8c1ae925" + integrity sha512-tT6pomf5Z/I7Jzxu8sScgrYBMK9bUFWd7Kbo6Fs1L0M13OOIJ/ZobGKS3Z7tQ8Re4lj+LnLXIQVZZxa3fhYKzA== + dependencies: + aws-ssl-profiles "^1.1.1" + denque "^2.1.0" + generate-function "^2.3.1" + iconv-lite "^0.7.0" + long "^5.2.1" + lru.min "^1.0.0" + named-placeholders "^1.1.3" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + mysql@^2.18.1: version "2.18.1" resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" @@ -2483,6 +2589,13 @@ mysql@^2.18.1: safe-buffer "5.1.2" sqlstring "2.3.1" +named-placeholders@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351" + integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w== + dependencies: + lru-cache "^7.14.1" + napi-build-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" @@ -2774,6 +2887,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pump@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" @@ -3030,6 +3148,11 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + serialize-javascript@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" @@ -3196,6 +3319,11 @@ sqlstring@2.3.1: resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" integrity sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ== +sqlstring@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" + integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + squel@^5.13.0: version "5.13.0" resolved "https://registry.yarnpkg.com/squel/-/squel-5.13.0.tgz#09cc73e91f0d0e326482605ee76e3b7ac881ddf6"