feat: swagger

This commit is contained in:
Kirill 2022-12-22 10:45:44 +00:00 committed by Mikhail
parent bda96dbd14
commit 8743492bc2
58 changed files with 1336 additions and 233 deletions

@ -7,38 +7,6 @@
2) yarn dev - запуск проекта в режиме разработки
```
## Переменные окружения сервиса:
# Ссылки на другую документацию:
**Для конфигурации сервера**:
```
ENVIRONMENT - application environment
HTTP_HOST - service host
HTTP_PORT - service port
PUBLIC_ACCESS_SECRET_KEY - secret to verify private access secret key
```
**Для конфигурации базы данных**:
```
DB_HOST - mongo host
DB_PORT - mongo port
DB_USERNAME - mongo username
DB_PASSWORD - mongo password
DB_NAME - database name
```
**Для подключения к сервису авторизации**
```
AUTH_SERVICE_HOST - auth service host
AUTH_SERVICE_PORT - auth service port
```
## Среды окружения
```
development - среда для разработки
staging - среда для тестирования продукта
production - среда продакшена
```
- [**Переменные окружения сервиса**](./docs/environment/README.md)

@ -0,0 +1,35 @@
# Переменные окружения сервиса:
**Для конфигурации сервера**:
```
ENVIRONMENT - application environment
HTTP_HOST - service host
HTTP_PORT - service port
PUBLIC_ACCESS_SECRET_KEY - secret to verify private access secret key
```
**Для конфигурации базы данных**:
```
DB_HOST - mongo host
DB_PORT - mongo port
DB_USERNAME - mongo username
DB_PASSWORD - mongo password
DB_NAME - database name
```
**Для подключения к сервису авторизации**
```
AUTH_SERVICE_HOST - auth service host
AUTH_SERVICE_PORT - auth service port
```
## Среды окружения
```
development - среда для разработки
staging - среда для тестирования продукта
production - среда продакшена
```

@ -37,6 +37,8 @@
"devDependencies": {
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@fastify/swagger": "^8.2.1",
"@fastify/swagger-ui": "^1.3.0",
"@types/bcryptjs": "^2.4.2",
"@types/jest": "^29.2.3",
"@types/jsonwebtoken": "^8.5.9",

@ -1,13 +1,13 @@
import { Router } from "@/server/router";
import { setAccountRoutes } from "@/routes/account.routes";
import { setPrivilegeRoutes } from "@/routes/privilege.routes";
import { setRoleRoutes } from "@/routes/role.routes";
import { setTariffRoutes } from "@/routes/tariff.routes";
import type { FastifyInstance } from "fastify";
export const combineRoutes = (server: FastifyInstance): void => {
server.register(setRoleRoutes, { prefix: "/role" });
server.register(setAccountRoutes, { prefix: "/account" });
server.register(setPrivilegeRoutes, { prefix: "/privilege" });
server.register(setTariffRoutes, { prefix: "/tariff" });
export const combineRoutes = (router: Router): void => {
router.group("/role", setRoleRoutes);
router.group("/account", setAccountRoutes);
router.group("/privilege", setPrivilegeRoutes);
router.group("/tariff", setTariffRoutes);
};

@ -1,20 +1,7 @@
import path from "path";
import dotenv from "dotenv";
import { DEFAULT } from "@/constants/default";
import type { Environment } from "@/types/environment";
const defineEnvironment = (): Environment => {
const environments: Environment[] = ["development", "staging", "production"];
const environment = process.env.ENVIRONMENT as Environment | undefined;
if (environment && environments.includes(environment)) {
return environment;
}
return DEFAULT.environment;
};
import { defineEnvironment } from "./define-environment";
export const configureENV = () => {
const environment = defineEnvironment();

@ -0,0 +1,14 @@
import { DEFAULT } from "@/constants/default";
import type { Environment } from "@/types/environment";
export const defineEnvironment = (): Environment => {
const environments: Environment[] = ["development", "staging", "production"];
const environment = process.env.ENVIRONMENT as Environment | undefined;
if (environment && environments.includes(environment)) {
return environment;
}
return DEFAULT.environment;
};

@ -1,8 +1,11 @@
import cors from "@fastify/cors";
import cookie from "@fastify/cookie";
import jwt from "@fastify/jwt";
import swagger from "@fastify/swagger";
import swaggerUI from "@fastify/swagger-ui";
import printRoutes from "@/plugins/print-routes";
import { CONFIGURATION } from "@/constants/configuration";
import type { FastifyInstance } from "fastify";
import type { PluginsOptions } from "@/types/configuration/plugins-options";
@ -11,6 +14,33 @@ export const registerFastifyPlugins = (fastify: FastifyInstance, options: Plugin
fastify.register(cors, options.cors);
fastify.register(cookie, options.cookie);
fastify.register(jwt, options.jwt);
fastify.register(swagger, {
openapi: {
info: {
title: "Hub admin backend",
description: "Тестирование сервиса админ панели хаба",
version: "0.1.0",
},
servers: [{ url: `http://${CONFIGURATION.http.host || "localhost"}:${CONFIGURATION.http.port}` }],
components: {
securitySchemes: {
bearer: {
type: "http",
bearerFormat: "JWT",
scheme: "bearer",
},
},
},
},
hideUntagged: true,
});
fastify.register(swaggerUI, {
routePrefix: "/swagger",
uiConfig: {
docExpansion: "full",
deepLinking: false,
},
});
fastify.register(printRoutes);
return fastify;

@ -5,10 +5,10 @@ import { AccountModel } from "@/models/account.model";
import { getUser } from "@/clients/auth";
import { validateEmptyFields } from "@/utils/validate-empty-fields";
import type { FastifyReply } from "fastify";
import type { CreateAccountRequest, GetAccountRequest } from "./types";
import type { FastifyReply, FastifyRequest } from "fastify";
import type { GetAccountRequest } from "./types";
export const createAccount = async (request: CreateAccountRequest, reply: FastifyReply) => {
export const createAccount = async (request: FastifyRequest, reply: FastifyReply) => {
if (!Types.ObjectId.isValid(request.user.id)) {
reply.status(400);
return new Error("invalid user id");

@ -1,6 +1,7 @@
import type { FastifyRequest } from "fastify";
import type { CreateAccountRoute, GetAccountRoute } from "@/types/routes/account-routes.type";
export type CreateAccountRequest = FastifyRequest<CreateAccountRoute>;
export type GetAccountRequest = FastifyRequest<GetAccountRoute>;
export type GetAccountRequest = FastifyRequest<{
Params?: {
userId?: string;
};
}>;

@ -2,7 +2,7 @@ import type { FastifyRequest, FastifyReply, HookHandlerDoneFunction as Done } fr
export const verifyUser = async (request: FastifyRequest, reply: FastifyReply, done: Done) => {
try {
const { id } = await request.jwtVerify<{ id?: string }>({ onlyCookie: true });
const { id } = await request.jwtVerify<{ id?: string }>();
if (!id) {
reply.status(401);

@ -15,7 +15,11 @@ export const validatePrivilege = (privilege: RawPrivilege): Error | null => {
return null;
};
export const defineGetAllPrivilegiesFormat = (query: string): GetAllPrivilegiesFormat => {
export const defineGetAllPrivilegiesFormat = (query?: string): GetAllPrivilegiesFormat => {
if (!query) {
return "array";
}
const formats: GetAllPrivilegiesFormat[] = ["array", "map"];
const findedFormat = formats.find((format) => query === format);

@ -53,7 +53,7 @@ export const registerPrivilege = async (request: RegisterPrivilegeRequest, reply
};
export const getAllPrivilegies = async (request: GetAllPrivilegiesRequest, reply: FastifyReply) => {
const format = defineGetAllPrivilegiesFormat(request.query?.format || "");
const format = defineGetAllPrivilegiesFormat(request.query?.format);
const privilegies = await PrivilegeModel.find({}).lean();
if (format === "array") {
@ -69,6 +69,7 @@ export const getAllPrivilegies = async (request: GetAllPrivilegiesRequest, reply
}
reply.status(500);
return new Error("format not defined");
};

@ -73,33 +73,32 @@ export const removeRole = async (request: RemoveRoleRequest, reply: FastifyReply
};
export const getRole = async (request: GetRoleRequest, reply: FastifyReply) => {
const { name, id } = request.body || {};
const { query } = request.params || {};
if (!name && !id && !query) {
reply.status(400);
return new Error("either name or id must be filled");
}
if (name || id) {
return RoleModel.findOne({
$or: [
{ name, isDeleted: false },
{ id, isDeleted: false },
],
}).lean();
}
if (!query) {
reply.status(400);
return new Error("query is empty");
}
if (Types.ObjectId.isValid(query)) {
return RoleModel.findOne({ _id: new Types.ObjectId(query), isDeleted: false }).lean();
const role = await RoleModel.findOne({ _id: new Types.ObjectId(query), isDeleted: false }).lean();
if (!role) {
reply.status(404);
return new Error("role not found");
}
return role;
}
return RoleModel.findOne({ name: query, isDeleted: false }).lean();
const role = await RoleModel.findOne({ name: query, isDeleted: false }).lean();
if (!role) {
reply.status(404);
return new Error("role not found");
}
return role;
};
export const replaceRole = async (request: UpdateRoleRequest, reply: FastifyReply) => {

@ -9,9 +9,6 @@ export type CreateRoleRequest = FastifyRequest<{ Body: RoleRequest }>;
export type RemoveRoleRequest = FastifyRequest<{ Body: { id?: string } }>;
export type GetRoleRequest = FastifyRequest<{
Body?: Pick<RoleRequest, "name"> & {
id?: string;
};
Params: {
query?: string;
};

@ -53,12 +53,8 @@ export const createTariff = async (request: CreateTariffRequest, reply: FastifyR
}
}
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege[]>>((accamulator, privilege) => {
if (!accamulator[privilege.privilegeId]) {
accamulator[privilege.privilegeId] = [];
}
accamulator[privilege.privilegeId].push(privilege);
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege>>((accamulator, privilege) => {
accamulator[privilege.privilegeId] = privilege;
return accamulator;
}, {});

@ -1,5 +1,5 @@
import type { FastifyRequest } from "fastify";
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
import type { TariffMessage } from "@/types/messages/tariff-message.type";
export type GetTariffRequest = FastifyRequest<{
Params?: {
@ -7,5 +7,11 @@ export type GetTariffRequest = FastifyRequest<{
};
}>;
export type CreateTariffRequest = FastifyRequest<CreateTariffRoute>;
export type ReplaceTariffRequest = FastifyRequest<ReplaceTariffRoute>;
export type CreateTariffRequest = FastifyRequest<{
Body?: TariffMessage;
}>;
export type ReplaceTariffRequest = FastifyRequest<{
Body?: TariffMessage;
Params?: { id?: string };
}>;

@ -15,7 +15,7 @@ const server = new Server({
jwt: {
secret: {
public: CONFIGURATION.service.publicAccessSecretKey,
private: "",
private: "secret",
},
verify: {
allowedIss: "pena-auth-service",

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { EloquentSchema } from "./eloquent-model.schema";
import { eloquentSchema } from "./eloquent.schema";
import type { Account } from "@/types/models/account.type";
@ -22,7 +22,7 @@ const schema: SchemaDefinition<Account> = {
type: String,
default: "user",
},
...EloquentSchema,
...eloquentSchema,
};
const schemaSettings = {

@ -1,32 +0,0 @@
import { Schema, SchemaDefinition } from "mongoose";
import type { EloquentModel } from "@/types/models/eloquent-model.type";
export const schema: SchemaDefinition<EloquentModel> = {
createdAt: {
type: Date,
required: true,
default: Date.now,
},
updatedAt: {
type: Date,
required: true,
default: Date.now,
},
deletedAt: {
type: Date,
required: false,
},
isDeleted: {
type: Boolean,
required: true,
default: false,
},
};
const schemaSettings = {
versionKey: false,
collection: "privilegies",
};
export const EloquentSchema = new Schema<EloquentModel>(schema, schemaSettings);

@ -0,0 +1,25 @@
import { SchemaDefinition } from "mongoose";
import type { Eloquent } from "@/types/models/eloquent.type";
export const eloquentSchema: SchemaDefinition<Eloquent> = {
createdAt: {
type: Date,
required: true,
default: Date.now,
},
updatedAt: {
type: Date,
required: true,
default: Date.now,
},
deletedAt: {
type: Date,
required: false,
},
isDeleted: {
type: Boolean,
required: true,
default: false,
},
};

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { EloquentSchema } from "./eloquent-model.schema";
import { eloquentSchema } from "./eloquent.schema";
import type { Privilege } from "@/types/models/privilege.type";
@ -37,7 +37,7 @@ const schema: SchemaDefinition<Privilege> = {
type: Number,
required: true,
},
...EloquentSchema,
...eloquentSchema,
};
const schemaSettings = {

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { EloquentSchema } from "./eloquent-model.schema";
import { eloquentSchema } from "./eloquent.schema";
import type { Role } from "@/types/models/role.type";
@ -14,7 +14,7 @@ const schema: SchemaDefinition<Role> = {
of: Boolean,
default: {},
},
...EloquentSchema,
...eloquentSchema,
};
const schemaSettings = {

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { EloquentSchema } from "./eloquent-model.schema";
import { eloquentSchema } from "./eloquent.schema";
import { PrivilegeSchema } from "./privilege.model";
import type { Tariff } from "@/types/models/tariff.type";
@ -23,7 +23,7 @@ const schema: SchemaDefinition<Tariff> = {
of: PrivilegeSchema,
default: {},
},
...EloquentSchema,
...eloquentSchema,
};
const schemaSettings = {

@ -8,7 +8,7 @@ type PrintRoutesRouteOptions = {
const METHODS_ORDER = ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS"];
const printRoutes = (routes: Array<RouteOptions & PrintRoutesRouteOptions> = []) => {
const printRoutes = (routes: Array<RouteOptions & PrintRoutesRouteOptions> = [], isSwagger = false) => {
if (routes.length === 0) {
return;
}
@ -21,8 +21,9 @@ const printRoutes = (routes: Array<RouteOptions & PrintRoutesRouteOptions> = [])
return accamulator + `${methodsValue}\t${route.url}\n`;
}, "");
const swaggerOutput = isSwagger ? "GET\t/swagger\n" : "";
console.info(`\n\nAvailable routes:\n${output}`);
console.info(`\n\nAvailable routes:\n${output + swaggerOutput}`);
};
export default fastifyPlugin(
@ -34,7 +35,7 @@ export default fastifyPlugin(
});
instance.addHook("onReady", (next) => {
printRoutes(routes);
printRoutes(routes, !!instance.swagger);
next();
});

@ -1,16 +1,11 @@
import { Router } from "@/server/router";
import { createAccount, getAccount } from "@/handlers/account";
import { verifyUser } from "@/handlers/auth/middleware";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import type { GetAccountRoute, CreateAccountRoute } from "@/types/routes/account-routes.type";
import { createAccountSchema, getAccountSchema } from "@/swagger/account";
export const setAccountRoutes = <T = FastifyPluginOptions>(
server: FastifyInstance,
opts: T,
done: () => void
): void => {
server.get<GetAccountRoute>("/:userId", getAccount);
server.post<CreateAccountRoute>("/", { preHandler: [verifyUser] }, createAccount);
done();
export const setAccountRoutes = (router: Router): void => {
router.get("/:userId", getAccount, { schema: getAccountSchema });
router.post("/", createAccount, { preHandler: [verifyUser], schema: createAccountSchema });
};

@ -1,3 +1,5 @@
import { Router } from "@/server/router";
import {
registerPrivilege,
getAllPrivilegies,
@ -6,18 +8,18 @@ import {
replacePrivilege,
} from "@/handlers/privilege";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import {
getPrivilegiesSchema,
getPrivilegeSchema,
getServicePrivilegiesSchema,
registerPrivilegeSchema,
replacePrivilegeSchema,
} from "@/swagger/privilege";
export const setPrivilegeRoutes = <T = FastifyPluginOptions>(
server: FastifyInstance,
opts: T,
done: () => void
): void => {
server.get("/all", getAllPrivilegies);
server.get("/:id", getPrivilege);
server.get("/service/:serviceKey", getServicePrivilegies);
server.post("/", registerPrivilege);
server.put("/", replacePrivilege);
done();
export const setPrivilegeRoutes = (router: Router): void => {
router.get("/", getAllPrivilegies, { schema: getPrivilegiesSchema });
router.get("/:id", getPrivilege, { schema: getPrivilegeSchema });
router.get("/service/:serviceKey", getServicePrivilegies, { schema: getServicePrivilegiesSchema });
router.post("/", registerPrivilege, { schema: registerPrivilegeSchema });
router.put("/", replacePrivilege, { schema: replacePrivilegeSchema });
};

@ -1,3 +1,5 @@
import { Router } from "@/server/router";
import {
getRole,
getAllRoles,
@ -9,17 +11,24 @@ import {
restoreRole,
} from "@/handlers/roles";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import {
getRolesSchema,
getRoleSchema,
createRoleSchema,
restoreRoleSchema,
updateRoleSchema,
replaceRoleSchema,
removeRoleSchema,
deleteRoleSchema,
} from "@/swagger/role";
export const setRoleRoutes = <T = FastifyPluginOptions>(server: FastifyInstance, opts: T, done: () => void): void => {
server.get("/all", getAllRoles);
server.get("/:query", getRole);
server.post("/", createRole);
server.post("/restore", restoreRole);
server.patch("/:query", updateRole);
server.put("/:query", replaceRole);
server.delete("/", removeRole);
server.delete("/delete", deleteRole);
done();
export const setRoleRoutes = (router: Router): void => {
router.get("/", getAllRoles, { schema: getRolesSchema });
router.get("/:query", getRole, { schema: getRoleSchema });
router.post("/", createRole, { schema: createRoleSchema });
router.post("/restore", restoreRole, { schema: restoreRoleSchema });
router.patch("/:query", updateRole, { schema: updateRoleSchema });
router.put("/:query", replaceRole, { schema: replaceRoleSchema });
router.delete("/", removeRole, { schema: removeRoleSchema });
router.delete("/delete", deleteRole, { schema: deleteRoleSchema });
};

@ -1,14 +1,13 @@
import { Router } from "@/server/router";
import { createTariff, replaceTariff, getTariff, getTariffs } from "@/handlers/tariff";
import { verifyUser } from "@/handlers/auth/middleware";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
import { getTariffSchema, getTariffsSchema, createTariffsSchema, replaceTariffsSchema } from "@/swagger/tariff";
export const setTariffRoutes = <T = FastifyPluginOptions>(server: FastifyInstance, opts: T, done: () => void): void => {
server.get("/:id", getTariff);
server.get("/", getTariffs);
server.post<CreateTariffRoute>("/", { preHandler: [verifyUser] }, createTariff);
server.put<ReplaceTariffRoute>("/", { preHandler: [verifyUser] }, replaceTariff);
done();
export const setTariffRoutes = (router: Router): void => {
router.get("/:id", getTariff, { schema: getTariffSchema });
router.get("/", getTariffs, { schema: getTariffsSchema });
router.post("/", createTariff, { preHandler: [verifyUser], schema: createTariffsSchema });
router.put("/", replaceTariff, { preHandler: [verifyUser], schema: replaceTariffsSchema });
};

@ -1,6 +1,8 @@
import fastify, { FastifyInstance } from "fastify";
import { connect as mongoConnect } from "mongoose";
import { Router } from "./router";
import { registerFastifyPlugins } from "@/configuration/register-fastify-plugins";
import { combineRoutes } from "@/configuration/combine-routes";
import { constituteMongoURI } from "@/configuration/constitute-mongo-uri";
@ -43,7 +45,7 @@ export class Server {
registerFastifyPlugins(this.fastify, pluginsOptions);
}
combineRoutes(this.fastify);
combineRoutes(new Router(this.fastify));
}
public start = async () => {
@ -51,7 +53,10 @@ export class Server {
const databaseConnection = this.databaseOptions ? mongoConnect(constituteMongoURI(this.databaseOptions)) : null;
await Promise.all([databaseConnection, fasticyConnection])
.then(() => console.info(`server started on ${this.serverOptions?.host}:${this.serverOptions?.port}`))
.then(() => {
this.fastify.swagger();
console.info(`server started on ${this.serverOptions?.host}:${this.serverOptions?.port}`);
})
.catch((reason) => console.error(reason));
};

77
src/server/router.ts Normal file

@ -0,0 +1,77 @@
import type {
FastifyInstance,
RouteHandlerMethod,
RawRequestDefaultExpression,
RawReplyDefaultExpression,
RouteShorthandOptions,
FastifyPluginOptions,
RawServerDefault,
RouteGenericInterface,
} from "fastify";
type HandlerMethod<T extends RouteGenericInterface> = RouteHandlerMethod<
RawServerDefault,
RawRequestDefaultExpression<RawServerDefault>,
RawReplyDefaultExpression<RawServerDefault>,
T
>;
export class Router {
private fastifyInstance: FastifyInstance;
constructor(fastifyInstance: FastifyInstance) {
this.fastifyInstance = fastifyInstance;
}
public group = (path: string, combineRoutes: (router: Router) => void) => {
this.fastifyInstance.register(
(server: FastifyInstance, opts: FastifyPluginOptions, done: () => void) => {
const router = new Router(server);
combineRoutes(router);
done();
},
{ prefix: path }
);
};
public get = <T extends RouteGenericInterface>(
path: string,
handler: HandlerMethod<T>,
options: RouteShorthandOptions
) => {
this.fastifyInstance.get(path, options, handler);
};
public post = <T extends RouteGenericInterface>(
path: string,
handler: HandlerMethod<T>,
options: RouteShorthandOptions
) => {
this.fastifyInstance.post(path, options, handler);
};
public put = <T extends RouteGenericInterface>(
path: string,
handler: HandlerMethod<T>,
options: RouteShorthandOptions
) => {
this.fastifyInstance.put(path, options, handler);
};
public patch = <T extends RouteGenericInterface>(
path: string,
handler: HandlerMethod<T>,
options: RouteShorthandOptions
) => {
this.fastifyInstance.patch(path, options, handler);
};
public delete = <T extends RouteGenericInterface>(
path: string,
handler: HandlerMethod<T>,
options: RouteShorthandOptions
) => {
this.fastifyInstance.delete(path, options, handler);
};
}

@ -0,0 +1,19 @@
import { getAccountParams } from "./inputs";
import { getAccountResponse, createAccountResponse } from "./responses";
import type { SwaggerSchema } from "@/types/swagger.type";
export const getAccountSchema: SwaggerSchema = {
summary: "Получение информации об аккаунте",
description: "Получение аккаунта по ID",
tags: ["account"],
params: getAccountParams,
response: getAccountResponse,
};
export const createAccountSchema: SwaggerSchema = {
summary: "Создание аккаунта",
tags: ["account"],
response: createAccountResponse,
security: [{ bearer: [] }],
};

@ -0,0 +1,11 @@
import type { SwaggerMessage } from "@/types/swagger.type";
export const getAccountParams: SwaggerMessage = {
type: "object",
properties: {
userId: {
type: "string",
description: "ID пользователя",
},
},
};

@ -0,0 +1,46 @@
import type { SwaggerMessage } from "@/types/swagger.type";
export const account: SwaggerMessage = {
description: "Аккаунт",
type: "object",
properties: {
userId: { type: "string" },
nickname: { type: "string" },
avatar: { type: "string" },
role: { type: "string" },
isDeleted: { type: "boolean" },
createdAt: {
type: "string",
format: "date-time",
},
updatedAt: {
type: "string",
format: "date-time",
},
deletedAt: {
type: "string",
format: "date-time",
},
},
examples: [
{
userId: "507f1f77bcf86cd799439011",
nickname: "Ivanov Ivan Ivanovich",
avatar: "/media/avatar/default-avatar.jpg",
role: "user",
isDeleted: false,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2017-07-21T17:32:28Z",
},
{
userId: "507f1f77bcf86cd799439011",
nickname: "Ivanov Ivan Ivanovich",
avatar: "/media/avatar/default-avatar.jpg",
role: "user",
isDeleted: true,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2019-04-14T15:32:15Z",
deletedAt: "2021-08-17T13:23:44Z",
},
],
};

@ -0,0 +1,18 @@
import { swaggerError } from "@/utils/swagger-error";
import { account } from "./models";
import type { SwaggerMessage } from "@/types/swagger.type";
export const getAccountResponse: Record<string, SwaggerMessage> = {
200: account,
400: swaggerError(400, "invalid user id"),
};
export const createAccountResponse: Record<string, SwaggerMessage> = {
200: account,
400: swaggerError(400, "invalid user id"),
401: swaggerError(401, "invalid token"),
404: swaggerError(404, "user not found"),
409: swaggerError(409, "account already exist"),
};

@ -0,0 +1,46 @@
import { privilegeBody, getPrivilegeParams, getServicePrivilegiesParams, getPrivilegiesQuery } from "./inputs";
import {
getPrivilegeReponse,
getPrivilegiesReponse,
getAllPrivilegiesReponse,
registerPrivilegeResponse,
replacePrivilegeResponse,
} from "./responses";
import type { SwaggerSchema } from "@/types/swagger.type";
export const getPrivilegiesSchema: SwaggerSchema = {
summary: "Получение всех привелегий",
description: "Получение всех привелегий в разном формате",
tags: ["privilege"],
querystring: getPrivilegiesQuery,
response: getAllPrivilegiesReponse,
};
export const getPrivilegeSchema: SwaggerSchema = {
summary: "Получение привилегии по ID",
tags: ["privilege"],
params: getPrivilegeParams,
response: getPrivilegeReponse,
};
export const getServicePrivilegiesSchema: SwaggerSchema = {
summary: "Получение привилегий сервиса",
tags: ["privilege"],
params: getServicePrivilegiesParams,
response: getPrivilegiesReponse,
};
export const registerPrivilegeSchema: SwaggerSchema = {
summary: "Регистрация привелегии сервиса",
tags: ["privilege"],
body: privilegeBody,
response: registerPrivilegeResponse,
};
export const replacePrivilegeSchema: SwaggerSchema = {
summary: "Замена привилегии сервиса",
tags: ["privilege"],
body: privilegeBody,
response: replacePrivilegeResponse,
};

@ -0,0 +1,62 @@
import type { SwaggerMessage } from "@/types/swagger.type";
export const privilegeBody: SwaggerMessage = {
type: "object",
required: ["name", "privilegeId", "serviceKey", "description", "type", "value", "price"],
properties: {
name: { type: "string" },
privilegeId: { type: "string" },
serviceKey: { type: "string" },
description: { type: "string" },
type: { type: "string" },
value: { type: "string" },
price: { type: "number" },
},
examples: [
{
name: "507f1f77bcf86cd799439011",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
},
],
};
export const getPrivilegeParams: SwaggerMessage = {
type: "object",
required: ["id"],
properties: {
id: {
type: "string",
description: "ID привилегии",
},
},
examples: [{ id: "507f1f77bcf86cd799439011" }],
};
export const getServicePrivilegiesParams: SwaggerMessage = {
type: "object",
required: ["serviceKey"],
properties: {
serviceKey: {
type: "string",
description: "ID привилегии",
},
},
examples: [{ id: "507f1f77bcf86cd799439011" }],
};
export const getPrivilegiesQuery: SwaggerMessage = {
type: "object",
properties: {
format: {
type: "string",
description:
"Есть два формата списка привелегий, в виде массива и в виде объекта ключ-значение, где ключём является ключ сервиса",
},
},
examples: [{ format: "array" }, { format: "map" }],
};

@ -0,0 +1,103 @@
import type { SwaggerMessage, SwaggerValueType } from "@/types/swagger.type";
const privilegeExamples: SwaggerValueType[] = [
{
name: "use count",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
isDeleted: false,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2017-07-21T17:32:28Z",
},
{
name: "use count",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
isDeleted: false,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2017-07-21T17:32:28Z",
},
];
export const privilege: SwaggerMessage = {
type: "object",
description: "Привилегия",
properties: {
name: { type: "string" },
privilegeId: { type: "string" },
serviceKey: { type: "string" },
description: { type: "string" },
type: { type: "string" },
value: { type: "string" },
price: { type: "number" },
isDeleted: { type: "boolean" },
createdAt: {
type: "string",
format: "date-time",
},
updatedAt: {
type: "string",
format: "date-time",
},
deletedAt: {
type: "string",
format: "date-time",
},
},
examples: privilegeExamples,
};
export const privilegiesMessage: SwaggerMessage = {
type: "object",
description: "Привилегии",
oneOf: [
{
type: "array",
items: privilege,
},
{
type: "object",
additionalProperties: privilege,
},
],
examples: [
privilegeExamples,
{
"docx-templater-service": [
{
name: "use count",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
isDeleted: false,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2017-07-21T17:32:28Z",
},
{
name: "use count",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
isDeleted: true,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2019-04-14T15:32:15Z",
deletedAt: "2021-08-17T13:23:44Z",
},
],
},
],
};

@ -0,0 +1,35 @@
import { swaggerError } from "@/utils/swagger-error";
import { privilege, privilegiesMessage } from "./models";
import type { SwaggerMessage } from "@/types/swagger.type";
export const getAllPrivilegiesReponse: Record<string, SwaggerMessage> = {
200: privilegiesMessage,
500: swaggerError(500, "format not defined"),
};
export const getPrivilegiesReponse: Record<string, SwaggerMessage> = {
200: {
type: "array",
items: privilege,
},
};
export const getPrivilegeReponse: Record<string, SwaggerMessage> = {
200: privilege,
400: swaggerError(400, "invalid id"),
404: swaggerError(404, "privilege not found"),
};
export const registerPrivilegeResponse: Record<string, SwaggerMessage> = {
200: privilege,
400: swaggerError(400, "price must be a number"),
409: swaggerError(409, "privilege already exist"),
};
export const replacePrivilegeResponse: Record<string, SwaggerMessage> = {
200: privilege,
400: swaggerError(400, "invalid 'type' value"),
404: swaggerError(404, "privilege not found"),
};

71
src/swagger/role/index.ts Normal file

@ -0,0 +1,71 @@
import { getRoleParams, roleBody, getRoleByIdBody, updateRoleBody } from "./inputs";
import {
getRolesReponse,
getRoleReponse,
createRoleReponse,
restoreRoleReponse,
updateRoleReponse,
removeRoleReponse,
} from "./responses";
import type { SwaggerSchema } from "@/types/swagger.type";
export const getRolesSchema: SwaggerSchema = {
summary: "Получение всех ролей",
tags: ["role"],
response: getRolesReponse,
};
export const getRoleSchema: SwaggerSchema = {
summary: "Получение роли",
tags: ["role"],
params: getRoleParams,
response: getRoleReponse,
};
export const createRoleSchema: SwaggerSchema = {
summary: "Создание роли",
tags: ["role"],
body: roleBody,
response: createRoleReponse,
};
export const restoreRoleSchema: SwaggerSchema = {
summary: "Восстановление удалённой роли",
description: "Восстанавливает удалённую роль, которая ещё существует в БД",
tags: ["role"],
body: getRoleByIdBody,
response: restoreRoleReponse,
};
export const updateRoleSchema: SwaggerSchema = {
summary: "Обновление роли",
tags: ["role"],
params: getRoleParams,
body: updateRoleBody,
response: updateRoleReponse,
};
export const replaceRoleSchema: SwaggerSchema = {
summary: "Замена роли",
tags: ["role"],
params: getRoleParams,
body: roleBody,
response: updateRoleReponse,
};
export const removeRoleSchema: SwaggerSchema = {
summary: "Удаление роли",
description: "Помечает роль удалённой, но не удаляет её окончательно",
tags: ["role"],
body: getRoleByIdBody,
response: removeRoleReponse,
};
export const deleteRoleSchema: SwaggerSchema = {
summary: "Получение всех ролей",
description: "Удаляет роль окончательно",
tags: ["role"],
body: getRoleByIdBody,
response: getRolesReponse,
};

@ -0,0 +1,58 @@
import type { SwaggerMessage } from "@/types/swagger.type";
export const getRoleParams: SwaggerMessage = {
type: "object",
required: ["query"],
properties: {
query: {
type: "string",
description: "query может принимать как id, так и name роли",
},
},
examples: [{ query: "507f1f77bcf86cd799439011" }, { query: "admin" }],
};
export const roleBody: SwaggerMessage = {
type: "object",
required: ["name", "permissions"],
properties: {
name: {
type: "string",
description: "Название роли",
},
permissions: {
type: "array",
description: "Массив разрешений",
items: { type: "string" },
},
},
examples: [{ name: "admin", permissions: ["read", "write"] }],
};
export const updateRoleBody: SwaggerMessage = {
type: "object",
properties: {
name: {
type: "string",
description: "Название роли",
},
permissions: {
type: "array",
description: "Массив разрешений",
items: { type: "string" },
},
},
examples: [{ name: "admin", permissions: ["read", "write"] }],
};
export const getRoleByIdBody: SwaggerMessage = {
type: "object",
required: ["id"],
properties: {
id: {
type: "string",
description: "ID роли",
},
},
examples: [{ id: "507f1f77bcf86cd799439011" }],
};

@ -0,0 +1,29 @@
import type { SwaggerMessage } from "@/types/swagger.type";
export const role: SwaggerMessage = {
type: "object",
description: "Роль",
properties: {
name: { type: "string" },
permissions: {
type: "object",
additionalProperties: { type: "boolean" },
},
},
examples: [
{
name: "user",
permissions: {
read: true,
write: false,
},
},
{
name: "admin",
permissions: {
read: true,
write: true,
},
},
],
};

@ -0,0 +1,50 @@
import { swaggerError } from "@/utils/swagger-error";
import { role } from "./models";
import type { SwaggerMessage } from "@/types/swagger.type";
export const getRolesReponse: Record<string, SwaggerMessage> = {
200: {
type: "array",
items: role,
},
};
export const getRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "query is empty"),
404: swaggerError(404, "role not found"),
};
export const createRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "field <name> is empty"),
409: swaggerError(409, "role already exist"),
};
export const restoreRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "wrong id"),
404: swaggerError(404, "role by id not found"),
409: swaggerError(409, "role not removed"),
};
export const updateRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "either name or permissions must be filled"),
404: swaggerError(404, "role not found"),
};
export const removeRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "wrong id"),
404: swaggerError(404, "role by id not found"),
409: swaggerError(409, "role already deleted"),
};
export const deleteRoleReponse: Record<string, SwaggerMessage> = {
200: role,
400: swaggerError(400, "wrong id"),
404: swaggerError(404, "role not found"),
};

@ -0,0 +1,30 @@
import { getTariffParams, tariffBody } from "./inputs";
import { getTariffReponse, getTariffsReponse, createTariffReponse, replaceTariffReponse } from "./responses";
import type { SwaggerSchema } from "@/types/swagger.type";
export const getTariffSchema: SwaggerSchema = {
summary: "Получение тарифа",
tags: ["tariff"],
params: getTariffParams,
response: getTariffReponse,
};
export const getTariffsSchema: SwaggerSchema = {
summary: "Получение списка тарифов",
tags: ["tariff"],
response: getTariffsReponse,
};
export const createTariffsSchema: SwaggerSchema = {
summary: "Создание тарифа",
tags: ["tariff"],
body: tariffBody,
response: createTariffReponse,
};
export const replaceTariffsSchema: SwaggerSchema = {
summary: "Замена тарифа",
tags: ["tariff"],
body: tariffBody,
response: replaceTariffReponse,
};

@ -0,0 +1,48 @@
import { privilege } from "@/swagger/privilege/models";
import type { SwaggerMessage } from "@/types/swagger.type";
export const getTariffParams: SwaggerMessage = {
type: "object",
required: ["id"],
properties: {
id: {
type: "string",
description: "ID тарифа",
},
},
examples: [{ id: "507f1f77bcf86cd799439011" }],
};
export const tariffBody: SwaggerMessage = {
type: "object",
description: "Тариф",
required: ["name", "price", "isCustom", "privilegies"],
properties: {
name: { type: "string" },
price: { type: "number" },
isCustom: { type: "boolean" },
privilegies: {
type: "array",
items: privilege,
},
},
examples: [
{
name: "user",
price: 14000,
isCustom: false,
privilegies: [
{
name: "507f1f77bcf86cd799439011",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
},
],
},
],
};

@ -0,0 +1,94 @@
import type { SwaggerMessage } from "@/types/swagger.type";
const privilege: SwaggerMessage = {
type: "object",
description: "Привилегия",
properties: {
name: { type: "string" },
privilegeId: { type: "string" },
serviceKey: { type: "string" },
description: { type: "string" },
type: { type: "string" },
value: { type: "string" },
price: { type: "number" },
},
examples: [
{
name: "507f1f77bcf86cd799439011",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
},
],
};
export const tariff: SwaggerMessage = {
type: "object",
description: "Тариф",
properties: {
name: { type: "string" },
price: { type: "number" },
isCustom: { type: "boolean" },
privilegies: {
type: "object",
additionalProperties: privilege,
},
isDeleted: { type: "boolean" },
createdAt: {
type: "string",
format: "date-time",
},
updatedAt: {
type: "string",
format: "date-time",
},
deletedAt: {
type: "string",
format: "date-time",
},
},
examples: [
{
name: "user",
price: 14000,
isCustom: false,
privilegies: {
"507f1f77bcf86cd799439011": {
name: "507f1f77bcf86cd799439011",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
},
},
isDeleted: false,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2017-07-21T17:32:28Z",
},
{
name: "user",
price: 14000,
isCustom: false,
privilegies: {
"507f1f77bcf86cd799439011": {
name: "507f1f77bcf86cd799439011",
privilegeId: "507f1f77bcf86cd799439011",
serviceKey: "docx-templater-service",
description: "Количество попыток использования",
type: "count",
value: "200",
price: 12300,
},
},
isDeleted: true,
createdAt: "2017-07-21T17:32:28Z",
updatedAt: "2019-04-14T15:32:15Z",
deletedAt: "2021-08-17T13:23:44Z",
},
],
};

@ -0,0 +1,30 @@
import { swaggerError } from "@/utils/swagger-error";
import { tariff } from "./models";
import type { SwaggerMessage } from "@/types/swagger.type";
export const getTariffReponse: Record<string, SwaggerMessage> = {
200: tariff,
400: swaggerError(400, "invalid id"),
404: swaggerError(404, "tariff not found"),
};
export const getTariffsReponse: Record<string, SwaggerMessage> = {
200: {
type: "array",
items: tariff,
},
};
export const createTariffReponse: Record<string, SwaggerMessage> = {
200: tariff,
400: swaggerError(400, "invalid 'price' value"),
404: swaggerError(404, "privilege with id <privilegeId> not found"),
};
export const replaceTariffReponse: Record<string, SwaggerMessage> = {
200: tariff,
400: swaggerError(400, "invalid id"),
404: swaggerError(404, "tariff not found"),
};

@ -1,10 +1,10 @@
import type { EloquentModel } from "../models/eloquent-model.type";
import type { Eloquent } from "../models/eloquent.type";
import type { Privilege } from "../models/privilege.type";
import type { Tariff } from "../models/tariff.type";
import type { ObjectWithPossibleFields } from "../object-with-possible-fields";
export type TariffMessage = ObjectWithPossibleFields<
Omit<Tariff, keyof EloquentModel | "privilegies"> & {
Omit<Tariff, keyof Eloquent | "privilegies"> & {
privilegies: Privilege[];
}
>;

@ -1,7 +1,8 @@
export type Account = {
import type { Eloquent } from "./eloquent.type";
export type Account = Eloquent & {
userId: string;
nickname: string;
avatar: string;
role: string;
privilegies: Record<string, string>;
};

@ -1,4 +1,4 @@
export type EloquentModel = {
export type Eloquent = {
createdAt: Date;
updatedAt: Date;
deletedAt: Date;

@ -1,6 +1,6 @@
import type { EloquentModel } from "./eloquent-model.type";
import type { Eloquent } from "./eloquent.type";
export type Privilege = EloquentModel & {
export type Privilege = Eloquent & {
name: string;
privilegeId: string;
serviceKey: string;

@ -1,6 +1,6 @@
import type { EloquentModel } from "./eloquent-model.type";
import type { Eloquent } from "./eloquent.type";
export type Role = EloquentModel & {
export type Role = Eloquent & {
name: string;
permissions: Record<string, boolean>;
};

@ -1,9 +1,9 @@
import type { Privilege } from "./privilege.type";
import type { EloquentModel } from "./eloquent-model.type";
import type { Eloquent } from "./eloquent.type";
export type Tariff = EloquentModel & {
export type Tariff = Eloquent & {
name: string;
price: number;
isCustom: boolean;
privilegies: Record<string, Privilege>;
privilegies: Record<string, Omit<Privilege, keyof Eloquent>>;
};

@ -1,6 +1,6 @@
import type { EloquentModel } from "./eloquent-model.type";
import type { Eloquent } from "./eloquent.type";
export type User = EloquentModel & {
export type User = Eloquent & {
_id: string;
login: string;
email: string;

@ -1,13 +0,0 @@
import type { RequestGenericInterface } from "fastify";
export type GetAccountRoute = RequestGenericInterface & {
Params?: {
userId?: string;
};
};
export type CreateAccountRoute = RequestGenericInterface & {
Params?: {
userId?: string;
};
};

@ -1,11 +0,0 @@
import type { RequestGenericInterface } from "fastify";
import type { TariffMessage } from "../messages/tariff-message.type";
export type CreateTariffRoute = RequestGenericInterface & {
Body?: TariffMessage;
};
export type ReplaceTariffRoute = RequestGenericInterface & {
Body?: TariffMessage;
Params?: { id?: string };
};

48
src/types/swagger.type.ts Normal file

@ -0,0 +1,48 @@
import type { FastifySchema } from "fastify";
export type SwaggerValueType =
| string
| number
| boolean
| null
| SwaggerValueType[]
| { [key: string]: SwaggerValueType };
type SwaggerMediaType = "object" | "string" | "integer" | "boolean" | "array" | "number";
type SwaggerMediaFormat =
| "binary"
| "base64"
| "uuid"
| "email"
| "date"
| "date-time"
| "password"
| "byte"
| "uri"
| "hostname"
| "ipv4"
| "ipv6";
export type SwaggerMessage = {
type: SwaggerMediaType;
description?: string;
format?: SwaggerMediaFormat;
pattern?: string;
nullable?: boolean;
uniqueItems?: boolean;
writeOnly?: boolean;
readOnly?: boolean;
items?: SwaggerMessage;
additionalProperties?: SwaggerMessage;
properties?: Record<string, SwaggerMessage | Record<string, SwaggerValueType>>;
examples?: SwaggerValueType[];
required?: string[];
oneOf?: SwaggerMessage[];
allOf?: SwaggerMessage[];
};
export type SwaggerSchema = FastifySchema & {
params?: SwaggerMessage;
body?: SwaggerMessage;
response?: Record<number, SwaggerMessage>;
};

@ -0,0 +1,19 @@
import type { SwaggerMessage } from "@/types/swagger.type";
const STATUS_CODE_MAP: Record<number, string> = {
400: "Bad Request",
401: "Unauthorized",
404: "Not Found",
409: "Conflict",
500: "Internal Server Error",
};
export const swaggerError = (code: number, message: string): SwaggerMessage => ({
type: "object",
properties: {
statusCode: { type: "integer" },
error: { type: "string" },
message: { type: "string" },
},
examples: [{ statusCode: code, error: STATUS_CODE_MAP[code], message: message }],
});

208
yarn.lock

@ -1204,6 +1204,11 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@fastify/accept-negotiator@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff"
integrity sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==
"@fastify/ajv-compiler@^3.3.1":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.4.0.tgz#e001b7e234b5b704654b1d617d69fa63c348f2a7"
@ -1257,6 +1262,41 @@
fastify-plugin "^4.0.0"
steed "^1.1.3"
"@fastify/static@^6.0.0":
version "6.6.0"
resolved "https://registry.yarnpkg.com/@fastify/static/-/static-6.6.0.tgz#763244583abf7a4734bff7b1d1aef0ef445393fb"
integrity sha512-UiYSN2dUmDZ48M40xdIwY1dPwSSYD7c+wtoIQP8y7wyxCwcUtf1YT5/Q4n1uJsBF1fySvuo9njQZKlHeiKy4HQ==
dependencies:
"@fastify/accept-negotiator" "^1.0.0"
content-disposition "^0.5.3"
fastify-plugin "^4.0.0"
glob "^8.0.1"
p-limit "^3.1.0"
readable-stream "^4.0.0"
send "^0.18.0"
"@fastify/swagger-ui@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@fastify/swagger-ui/-/swagger-ui-1.3.0.tgz#967afd8bfa87a539b83ae6bc986f3589e9fdb98a"
integrity sha512-Q6vvIyTd1gj0h0IoDAAUX3SBBiL1pybXP0FmFbD4yLMcACIZ7xm8oHCf5lMc3rNC69KhbywuSst3iHgp23x8SA==
dependencies:
"@fastify/static" "^6.0.0"
fastify-plugin "^4.0.0"
openapi-types "^12.0.2"
rfdc "^1.3.0"
yaml "^2.1.3"
"@fastify/swagger@^8.2.1":
version "8.2.1"
resolved "https://registry.yarnpkg.com/@fastify/swagger/-/swagger-8.2.1.tgz#a5f744868c25c49bde2469f5412ce0405aac150a"
integrity sha512-nYP/3ncrI5YmaGiJf6m+CLdFrdlWSsASHBPqP9uN9/oFFwDJwdUtq0ylmvObxzqWNVt9zT50iT/uvIndVEsvbg==
dependencies:
fastify-plugin "^4.0.0"
json-schema-resolver "^2.0.0"
openapi-types "^12.0.0"
rfdc "^1.3.0"
yaml "^2.1.1"
"@humanwhocodes/config-array@^0.11.6":
version "0.11.7"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
@ -2184,6 +2224,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@ -2410,6 +2457,13 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
content-disposition@^0.5.3:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
conventional-changelog-angular@^5.0.11:
version "5.0.13"
resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c"
@ -2489,6 +2543,13 @@ dargs@^7.0.0:
resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"
integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==
debug@2.6.9, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@4, debug@4.x, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -2496,13 +2557,6 @@ debug@4, debug@4.x, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debu
dependencies:
ms "2.1.2"
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@ -2561,6 +2615,16 @@ denque@^2.1.0:
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-newline@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@ -2616,6 +2680,11 @@ ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.4.251:
version "1.4.284"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
@ -2631,6 +2700,11 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -2704,6 +2778,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -2899,6 +2978,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
@ -3179,6 +3263,11 @@ forwarded@0.2.0:
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@ -3307,6 +3396,17 @@ glob@^7.1.3, glob@^7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^8.0.1:
version "8.0.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e"
integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
global-dirs@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
@ -3432,6 +3532,17 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
https-proxy-agent@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@ -3499,7 +3610,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -4131,6 +4242,15 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json-schema-resolver@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz#d17fdf53560e6bc9af084b930fee27f6ce4a03b6"
integrity sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==
dependencies:
debug "^4.1.1"
rfdc "^1.1.4"
uri-js "^4.2.2"
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@ -4409,6 +4529,11 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@ -4431,6 +4556,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.2.tgz#0939d7d6f0898acbd1508abe534d1929368a8fff"
integrity sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==
dependencies:
brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@ -4669,6 +4801,13 @@ on-exit-leak-free@^2.1.0:
resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4"
integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
once@^1.3.0, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -4692,6 +4831,11 @@ open@^8.4.0:
is-docker "^2.1.1"
is-wsl "^2.2.0"
openapi-types@^12.0.0, openapi-types@^12.0.2:
version "12.1.0"
resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.0.tgz#bd01acc937b73c9f6db2ac2031bf0231e21ebff0"
integrity sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@ -4935,6 +5079,11 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
@ -5070,7 +5219,7 @@ reusify@^1.0.0, reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rfdc@^1.2.0, rfdc@^1.3.0:
rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
@ -5089,7 +5238,7 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -5161,11 +5310,35 @@ semver@~7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
send@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
set-cookie-parser@^2.4.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz#ddd3e9a566b0e8e0862aca974a6ac0e01349430b"
integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@ -5318,6 +5491,11 @@ stack-utils@^2.0.3:
dependencies:
escape-string-regexp "^2.0.0"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
steed@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/steed/-/steed-1.1.3.tgz#f1525dd5adb12eb21bf74749537668d625b9abc5"
@ -5546,6 +5724,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
@ -5847,6 +6030,11 @@ yaml@^1.10.0:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.1.1, yaml@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207"
integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==
yargs-parser@^20.2.3:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"