feat: better host restrictions

This commit is contained in:
sylv 2022-06-21 03:15:56 +08:00
parent ca63479776
commit 0ea831bb6b
22 changed files with 417 additions and 169 deletions

View File

@ -12,7 +12,8 @@
"scripts": {
"watch": "pnpm tsup src/main.ts src/migrations/* --watch --onSuccess \"node dist/main.js\" --target node16",
"build": "rm -rf ./dist && pnpm tsup src/main.ts src/types.ts src/migrations/* --dts --sourcemap --target node16",
"start": "node ./dist/main.js"
"start": "node ./dist/main.js",
"test": "jest"
},
"dependencies": {
"@fastify/cookie": "^6.0.0",
@ -59,15 +60,18 @@
"devDependencies": {
"@mikro-orm/cli": "^5.2.0",
"@swc/core": "^1.2.175",
"@sylo-digital/scripts": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"@types/bcrypt": "^5.0.0",
"@types/dedent": "^0.7.0",
"@types/jest": "^27.4.1",
"@types/luxon": "^2.3.2",
"@types/mime-types": "^2.1.1",
"@types/node": "16",
"@types/passport-jwt": "^3.0.6",
"@types/passport-local": "^1.0.34",
"@types/sharp": "^0.30.2",
"jest": "^28.1.0",
"nodemon": "^2.0.16",
"ts-node": "^10.7.0",
"tsup": "^5.12.6",
@ -79,5 +83,8 @@
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
},
"jest": {
"preset": "@sylo-digital/scripts/jest/node"
}
}

View File

@ -3,13 +3,19 @@ import escapeString from "escape-string-regexp";
import { HostService } from "../modules/host/host.service";
export class MicroHost {
constructor(url: string, tags?: string[], redirect?: string) {
this.url = url;
this.tags = tags;
this.redirect = redirect;
}
// https://regex101.com/r/ZR9rpp/1
@Matches(/^https?:\/\/[\d.:A-z{}-]+$/)
url: string;
@IsString({ each: true })
@IsOptional()
tags: string[] = [];
tags?: string[];
@IsUrl({ require_protocol: true })
@IsOptional()
@ -26,9 +32,14 @@ export class MicroHost {
private _pattern?: RegExp;
get pattern() {
if (this._pattern) return this._pattern;
const escaped = escapeString(this.normalised);
const pattern = escaped.replace("\\{\\{username\\}\\}", "(?<username>[a-z0-9-{}]+?)");
this._pattern = new RegExp(`^(https?:\\/\\/)?${pattern}\\/?`);
this._pattern = MicroHost.getWildcardPattern(this.url);
return this._pattern;
}
static getWildcardPattern(url: string) {
const normalised = HostService.normaliseHostUrl(url);
const escaped = escapeString(normalised);
const pattern = escaped.replace("\\{\\{username\\}\\}", "(?<username>[a-z0-9-{}]+?)");
return new RegExp(`^(https?:\\/\\/)?${pattern}\\/?`);
}
}

View File

@ -249,6 +249,15 @@
"nullable": false,
"mappedType": "string"
},
"hostname": {
"name": "hostname",
"type": "varchar(255)",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "string"
},
"content": {
"name": "content",
"type": "varchar(500000)",
@ -367,8 +376,8 @@
"length": 1024,
"mappedType": "string"
},
"host": {
"name": "host",
"hostname": {
"name": "hostname",
"type": "varchar(255)",
"unsigned": false,
"autoincrement": false,
@ -445,8 +454,8 @@
"nullable": false,
"mappedType": "string"
},
"host": {
"name": "host",
"hostname": {
"name": "hostname",
"type": "varchar(255)",
"unsigned": false,
"autoincrement": false,

View File

@ -0,0 +1,15 @@
import { Migration } from "@mikro-orm/migrations";
export class Migration20220620182830 extends Migration {
async up(): Promise<void> {
this.addSql('alter table "pastes" add column "hostname" varchar(255) null;');
this.addSql('alter table "links" rename column "host" to "hostname";');
this.addSql('alter table "files" rename column "host" to "hostname";');
}
async down(): Promise<void> {
this.addSql('alter table "pastes" drop column "hostname";');
this.addSql('alter table "links" rename column "hostname" to "host";');
this.addSql('alter table "files" rename column "hostname" to "host";');
}
}

View File

@ -28,7 +28,7 @@ export class AppController {
},
hosts: config.hosts
.filter((host) => {
if (!host.tags[0]) return true;
if (!host.tags || !host.tags[0]) return true;
return host.tags.every((tag) => tags.includes(tag));
})
.map((host) => ({

View File

@ -45,7 +45,7 @@ export class FileController {
@Get("file/:fileId")
async getFile(@Res() reply: FastifyReply, @Param("fileId") fileId: string, @Request() request: FastifyRequest) {
const file = await this.fileService.getFile(fileId, request.host);
const file = await this.fileService.getFile(fileId, request);
return reply.send(file);
}
@ -65,10 +65,16 @@ export class FileController {
@Headers("X-Micro-Paste-Shortcut") shortcut: string
) {
const user = await this.userService.getUser(userId);
if (!user) throw new ForbiddenException("Unknown user.");
if (!user) throw new ForbiddenException("Unknown user");
const upload = (await request.file()) as MultipartFile | undefined;
if (!upload) throw new BadRequestException("Missing upload.");
const possibleHosts = hosts.split(/, ?/g);
const hostUrl = randomItem(possibleHosts);
const host = await this.hostService.getHostFrom(hostUrl, user.tags);
if (shortcut === "true" && upload.mimetype === "text/plain") {
if (host) this.hostService.checkUserCanUploadTo(host, user);
const content = await upload.toBuffer();
const paste = this.pasteRepo.create({
content: content.toString(),
@ -76,15 +82,13 @@ export class FileController {
encrypted: false,
owner: userId,
extension: "txt",
hostname: host?.normalised,
});
await this.pasteRepo.persistAndFlush(paste);
return paste;
}
const possibleHosts = hosts.split(/, ?/g);
const hostUrl = randomItem(possibleHosts);
const host = await this.hostService.getHostFrom(hostUrl, user.tags);
const file = await this.fileService.createFile(upload, request, user, host);
return file;
}

View File

@ -13,18 +13,16 @@ import { checkThumbnailSupport } from "@ryanke/thumbnail-generator";
import mimeType from "mime-types";
import { config } from "../../config";
import { generateDeleteKey } from "../../helpers/generate-delete-key.helper";
import { WithHostname } from "../host/host.entity";
import { Thumbnail } from "../thumbnail/thumbnail.entity";
import { User } from "../user/user.entity";
import { FileMetadata } from "./file-metadata.embeddable";
@Entity({ tableName: "files" })
export class File {
export class File extends WithHostname {
@PrimaryKey()
id: string;
@Property({ nullable: true })
host?: string;
@Property()
type: string;
@ -71,7 +69,7 @@ export class File {
@Property({ persist: false })
get urls() {
const owner = this.owner.unwrap();
const host = this.host ? config.hosts.find((host) => host.normalised === this.host) : null;
const host = this.hostname ? config.hosts.find((host) => host.normalised === this.hostname) : null;
const baseUrl = host ? host.url.replace("{{username}}", owner.username) : config.rootHost.url;
return {
view: baseUrl + this.paths.view,
@ -102,14 +100,5 @@ export class File {
};
}
[OptionalProps]:
| "paths"
| "urls"
| "displayName"
| "createdAt"
| "thumbnail"
| "name"
| "deleteKey"
| "host"
| "extension";
[OptionalProps]: "paths" | "urls" | "displayName" | "createdAt" | "thumbnail" | "name" | "deleteKey" | "extension";
}

View File

@ -22,6 +22,7 @@ import { generateContentId } from "../../helpers/generate-content-id.helper";
import { getStreamType } from "../../helpers/get-stream-type.helper";
import { HostService } from "../host/host.service";
import { StorageService } from "../storage/storage.service";
import { User } from "../user/user.entity";
import { File } from "./file.entity";
@Injectable()
@ -34,9 +35,9 @@ export class FileService implements OnApplicationBootstrap {
private orm: MikroORM
) {}
async getFile(id: string, host: MicroHost) {
async getFile(id: string, request: FastifyRequest) {
const file = await this.fileRepo.findOneOrFail(id);
if (!this.hostService.canHostSendEntity(host, file)) {
if (file.hostname && !file.canSendTo(request)) {
throw new NotFoundException("Your file is in another castle.");
}
@ -62,9 +63,10 @@ export class FileService implements OnApplicationBootstrap {
async createFile(
multipart: Multipart,
request: FastifyRequest,
owner: { id: string; tags?: string[] },
owner: User,
host: MicroHost | undefined
): Promise<File> {
if (host) this.hostService.checkUserCanUploadTo(host, owner);
if (!request.headers["content-length"]) throw new BadRequestException('Missing "Content-Length" header.');
if (+request.headers["content-length"] >= config.uploadLimit) {
const size = xbytes(+request.headers["content-length"]);
@ -89,7 +91,7 @@ export class FileService implements OnApplicationBootstrap {
type: type,
name: multipart.filename,
owner: owner.id,
host: host?.normalised,
hostname: host?.normalised.replace("{{username}}", owner.username),
hash: hash,
size: size,
});
@ -99,7 +101,7 @@ export class FileService implements OnApplicationBootstrap {
}
async sendFile(fileId: string, request: FastifyRequest, reply: FastifyReply) {
const file = await this.getFile(fileId, request.host);
const file = await this.getFile(fileId, request);
const range = request.headers["content-range"] ? contentRange.parse(request.headers["content-range"]) : null;
const stream = this.storageService.createReadStream(file.hash, range);
if (range) reply.header("Content-Range", contentRange.format(range));

View File

@ -0,0 +1,56 @@
import { BeforeCreate, BeforeUpdate, Entity, EventArgs, Property } from "@mikro-orm/core";
import { FastifyRequest } from "fastify";
import { config } from "../../config";
function getHostFromRequest(request: FastifyRequest): string {
if (request.headers["x-forwarded-host"]) {
if (Array.isArray(request.headers["x-forwarded-host"])) {
return request.headers["x-forwarded-host"][0];
}
return request.headers["x-forwarded-host"];
}
if (request.headers["host"]) return request.headers["host"];
return request.hostname;
}
@Entity({ abstract: true })
export abstract class WithHostname {
@Property({ nullable: true })
hostname?: string;
getHost() {
if (!this.hostname) return config.rootHost;
const match = config.hosts.find((host) => host.pattern.test(this.hostname!));
if (match) return match;
return config.rootHost;
}
canSendTo(request: FastifyRequest) {
const hostname = getHostFromRequest(request);
if (!config.restrictFilesToHost) return true;
// root host can send all files
if (hostname === config.rootHost.normalised) return true;
if (this.hostname === hostname) return true;
if (this.hostname?.includes("{{username}}")) {
// old files have {{username}} in the persisted hostname, migrating them
// to the new format is too difficult so this does a dirty comparison
// that should work for most use cases.
const withoutWildcard = this.hostname.replace("{{username}}", "");
return hostname.endsWith(withoutWildcard);
}
return false;
}
@BeforeCreate()
@BeforeUpdate()
async onBeforePersist(args: EventArgs<WithHostname>) {
console.log("Before persist");
if (args.entity.hostname?.includes("{{username}}")) {
throw new Error("Host placeholders should be replaced before insert");
}
}
}

View File

@ -2,7 +2,7 @@ import { BadRequestException, ForbiddenException } from "@nestjs/common";
import normalizeUrl from "normalize-url";
import { MicroHost } from "../../classes/MicroHost";
import { config } from "../../config";
import { randomItem } from "../../helpers/random-item.helper";
import { User } from "../user/user.entity";
export class HostService {
formatHostUrl(url: string, username: string, path?: string | null) {
@ -18,12 +18,11 @@ export class HostService {
* @throws if the host could not be resolved.
*/
async getHostFrom(url: string | undefined, tags: string[] | null): Promise<MicroHost> {
console.log({ url });
if (!url) return config.rootHost;
const normalised = HostService.normaliseHostUrl(url);
for (const host of config.hosts) {
if (!host.pattern.test(normalised)) continue;
if (tags) {
if (tags && host.tags) {
const hasTags = host.tags.every((tag) => tags.includes(tag));
if (!hasTags) {
throw new ForbiddenException("Missing host authorisation.");
@ -36,18 +35,12 @@ export class HostService {
throw new BadRequestException(`Invalid host URL "${url}".`);
}
canHostSendEntity(host: MicroHost, entity: { host?: string }) {
// todo: if host.wildcard, we should check to make sure the file owner
// matches the given username in the request url. so uploads to
// sylver.is-fucking.gay can't be accessed on cyk.is-fucking.gay and vice versa
if (!config.restrictFilesToHost) return true;
// files without a host can be served on all hosts
if (!entity.host) return true;
// the host that the file was uploaded to can serve the file
if (entity.host === host.normalised) return true;
// root host can serve all files.
if (host.normalised === config.rootHost.normalised) return true;
return false;
checkUserCanUploadTo(host: MicroHost, user: User) {
if (host.tags && !host.tags.every((tag) => user.tags.includes(tag))) {
throw new ForbiddenException("You are not allowed to upload to that host.");
}
return true;
}
static normaliseHostUrl(url: string) {

View File

@ -1,21 +1,9 @@
import { InjectRepository } from "@mikro-orm/nestjs";
import { EntityRepository } from "@mikro-orm/postgresql";
import {
BadRequestException,
Controller,
Delete,
Get,
NotFoundException,
Param,
Post,
Query,
Request,
Res,
} from "@nestjs/common";
import { Controller, Get, Param, Request, Res } from "@nestjs/common";
import { FastifyReply, FastifyRequest } from "fastify";
import { UserId } from "../auth/auth.decorators";
import { LinkService } from "./link.service";
import { Link } from "./link.entity";
import { LinkService } from "./link.service";
@Controller()
export class LinkController {
@ -23,7 +11,7 @@ export class LinkController {
@Get("link/:id/go")
async followLink(@Param("id") id: string, @Request() request: FastifyRequest, @Res() reply: FastifyReply) {
const link = await this.linkService.getLink(id, request.host);
const link = await this.linkService.getLink(id, request);
link.clicks++;
await this.linkRepo.persistAndFlush(link);
reply.redirect(301, link.destination);
@ -31,6 +19,6 @@ export class LinkController {
@Get("link/:id")
async getLink(@Request() request: FastifyRequest, @Param("id") id: string) {
return this.linkService.getLink(id, request.host);
return this.linkService.getLink(id, request);
}
}

View File

@ -1,18 +1,16 @@
import { Entity, IdentifiedReference, ManyToOne, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core";
import { generateContentId } from "../../helpers/generate-content-id.helper";
import { WithHostname } from "../host/host.entity";
import { User } from "../user/user.entity";
@Entity({ tableName: "links" })
export class Link {
export class Link extends WithHostname {
@PrimaryKey({ type: String })
id = generateContentId();
@Property({ length: 1024 })
destination: string;
@Property({ nullable: true })
host?: string;
@Property()
clicks: number = 0;
@ -25,5 +23,5 @@ export class Link {
})
owner: IdentifiedReference<User>;
[OptionalProps]: "host" | "clicks" | "createdAt";
[OptionalProps]: "hostname" | "clicks" | "createdAt";
}

View File

@ -1,18 +1,17 @@
import { InjectRepository } from "@mikro-orm/nestjs";
import { EntityRepository } from "@mikro-orm/postgresql";
import { Injectable, NotFoundException, UnauthorizedException } from "@nestjs/common";
import { MicroHost } from "../../classes/MicroHost";
import { HostService } from "../host/host.service";
import { FastifyRequest } from "fastify";
import { Link } from "./link.entity";
@Injectable()
export class LinkService {
constructor(@InjectRepository(Link) private linkRepo: EntityRepository<Link>, private hostService: HostService) {}
constructor(@InjectRepository(Link) private linkRepo: EntityRepository<Link>) {}
async getLink(id: string, host: MicroHost | null) {
async getLink(id: string, request: FastifyRequest) {
const link = await this.linkRepo.findOneOrFail(id);
if (host !== null && !this.hostService.canHostSendEntity(host, link)) {
throw new NotFoundException("Your redirect is in another castle.");
if (link.hostname && !link.canSendTo(request)) {
throw new NotFoundException("Your link is in another castle.");
}
return link;

View File

@ -4,6 +4,7 @@ import {
BadRequestException,
Body,
Controller,
ForbiddenException,
Get,
HttpCode,
NotFoundException,
@ -15,7 +16,10 @@ import {
import { FastifyRequest } from "fastify";
import { config } from "../../config";
import { generateContentId, generateParanoidId } from "../../helpers/generate-content-id.helper";
import { UserId } from "../auth/auth.decorators";
import { OptionalJWTAuthGuard } from "../auth/guards/optional-jwt.guard";
import { HostService } from "../host/host.service";
import { UserService } from "../user/user.service";
import { CreatePasteDto, Paste } from "./paste.entity";
import { PasteService } from "./paste.service";
@ -23,19 +27,30 @@ import { PasteService } from "./paste.service";
export class PasteController {
constructor(
@InjectRepository(Paste) private pasteRepo: EntityRepository<Paste>,
private pasteService: PasteService
private pasteService: PasteService,
private userService: UserService,
private hostService: HostService
) {}
@Post()
@UseGuards(OptionalJWTAuthGuard)
async create(@Body() pasteBody: CreatePasteDto, @Req() request: FastifyRequest) {
async create(@UserId() userId: string, @Body() pasteBody: CreatePasteDto, @Req() request: FastifyRequest) {
const user = await this.userService.getUser(userId);
if (!user) throw new ForbiddenException("Unknown user");
if (request.host) this.hostService.checkUserCanUploadTo(request.host, user);
if (!request.user && !config.publicPastes) {
throw new BadRequestException("You must be logged in to create a paste on this instance.");
}
const id = pasteBody.paranoid ? generateParanoidId() : generateContentId();
const expiresAt = pasteBody.expiresAt ? new Date(pasteBody.expiresAt) : undefined;
if (expiresAt && expiresAt.getTime() < Date.now() + 1000) {
throw new BadRequestException("Paste expiry must be in the future or unset");
}
const paste = this.pasteRepo.create({
...pasteBody,
expiresAt: expiresAt,
owner: request.user,
id: id,
});
@ -45,8 +60,8 @@ export class PasteController {
}
@Get(":pasteId")
async get(@Param("pasteId") pasteId: string) {
return this.pasteService.getPaste(pasteId);
async get(@Param("pasteId") pasteId: string, @Req() request: FastifyRequest) {
return this.pasteService.getPaste(pasteId, request);
}
@Post(":pasteId/burn")

View File

@ -2,10 +2,11 @@ import { Entity, IdentifiedReference, ManyToOne, OptionalProps, PrimaryKey, Prop
import { IsBoolean, IsNumber, IsOptional, IsString, Length } from "class-validator";
import { config } from "../../config";
import { generateContentId } from "../../helpers/generate-content-id.helper";
import { WithHostname } from "../host/host.entity";
import { User } from "../user/user.entity";
@Entity({ tableName: "pastes" })
export class Paste {
export class Paste extends WithHostname {
@PrimaryKey({ type: String })
id = generateContentId();

View File

@ -1,11 +1,13 @@
import { MikroOrmModule } from "@mikro-orm/nestjs";
import { Module } from "@nestjs/common";
import { HostModule } from "../host/host.module";
import { UserModule } from "../user/user.module";
import { PasteController } from "./paste.controller";
import { Paste } from "./paste.entity";
import { PasteService } from "./paste.service";
@Module({
imports: [MikroOrmModule.forFeature([Paste])],
imports: [MikroOrmModule.forFeature([Paste]), HostModule, UserModule],
controllers: [PasteController],
providers: [PasteService],
})

View File

@ -1,7 +1,8 @@
import { EntityRepository } from "@mikro-orm/core";
import { InjectRepository } from "@mikro-orm/nestjs";
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
import { Cron, CronExpression } from "@nestjs/schedule";
import { FastifyRequest } from "fastify";
import { Paste } from "./paste.entity";
@Injectable()
@ -9,8 +10,12 @@ export class PasteService {
private log = new Logger("PasteService");
constructor(@InjectRepository(Paste) private pasteRepo: EntityRepository<Paste>) {}
async getPaste(pasteId: string) {
async getPaste(pasteId: string, request: FastifyRequest) {
const paste = await this.pasteRepo.findOneOrFail(pasteId);
if (!paste.canSendTo(request)) {
throw new BadRequestException("Your paste is in another castle.");
}
if (paste.expiresAt && paste.expiresAt.getTime() < Date.now()) {
await this.pasteRepo.removeAndFlush(paste);
throw new NotFoundException();

View File

@ -76,7 +76,7 @@ export class ThumbnailService {
.send(existing.data);
}
const file = await this.fileService.getFile(fileId, request.host);
const file = await this.fileService.getFile(fileId, request);
const thumbnail = await this.createThumbnail(file);
return reply
.header("X-Micro-Generated", "true")

View File

@ -1,7 +1,15 @@
import { GetServerSidePropsContext } from "next";
import { http } from "./http.helper";
export async function fetcher<T = unknown>(url: string): Promise<T> {
const response = await http(url);
export async function fetcher<T = unknown>(url: string, context?: GetServerSidePropsContext): Promise<T> {
const headers: Record<string, string> = {};
if (context) {
// for host matching we have to forward the host header
if (context.req.headers["user-agent"]) headers["User-Agent"] = context.req.headers["user-agent"];
if (context.req.headers["host"]) headers["x-forwarded-host"] = context.req.headers["host"];
}
const response = await http(url, { headers });
const text = await response.text();
try {
return JSON.parse(text);

View File

@ -141,7 +141,7 @@ export async function getServerSideProps(
// yes, im salty about it.
try {
const fallbackData = await fetcher<GetFileData>(`file/${context.query.fileId}`);
const fallbackData = await fetcher<GetFileData>(`file/${context.query.fileId}`, context);
return { props: { fallbackData } };
} catch (error) {
if (error instanceof HTTPError) {

View File

@ -2,7 +2,7 @@ import { GetPasteData } from "@ryanke/micro-api";
import { GetServerSidePropsContext, GetServerSidePropsResult } from "next";
import { useRouter } from "next/router";
import { Language } from "prism-react-renderer";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { BookOpen, Clock, Trash } from "react-feather";
import useSWR from "swr";
import { Container } from "../../components/container";
@ -154,7 +154,7 @@ export async function getServerSideProps(
context: GetServerSidePropsContext
): Promise<GetServerSidePropsResult<ViewPasteProps>> {
try {
const fallbackData = await fetcher<GetPasteData>(`paste/${context.query.pasteId}`);
const fallbackData = await fetcher<GetPasteData>(`paste/${context.query.pasteId}`, context);
return { props: { fallbackData } };
} catch (error) {
if (error instanceof HTTPError) {

View File

@ -39,9 +39,11 @@ importers:
'@ryanke/thumbnail-generator': workspace:^0.0.1
'@ryanke/venera': ^0.0.2
'@swc/core': ^1.2.175
'@sylo-digital/scripts': ^1.0.0
'@tsconfig/node16': ^1.0.2
'@types/bcrypt': ^5.0.0
'@types/dedent': ^0.7.0
'@types/jest': ^27.4.1
'@types/luxon': ^2.3.2
'@types/mime-types': ^2.1.1
'@types/node': '16'
@ -59,6 +61,7 @@ importers:
get-stream: ^6.0.1
http-status-codes: ^2.2.0
istextorbinary: ^6.0.0
jest: ^28.1.0
luxon: ^2.3.2
mime-types: ^2.1.35
ms: ^3.0.0-canary.1
@ -121,15 +124,18 @@ importers:
devDependencies:
'@mikro-orm/cli': 5.2.0_bxish3hg3dbguccecfvzdrv6hm
'@swc/core': 1.2.175
'@sylo-digital/scripts': 1.0.0_jest@28.1.1
'@tsconfig/node16': 1.0.2
'@types/bcrypt': 5.0.0
'@types/dedent': 0.7.0
'@types/jest': 27.5.2
'@types/luxon': 2.3.2
'@types/mime-types': 2.1.1
'@types/node': 16.10.1
'@types/passport-jwt': 3.0.6
'@types/passport-local': 1.0.34
'@types/sharp': 0.30.2
jest: 28.1.1_ljamerpmo44qvadngd6o6tmh2u
nodemon: 2.0.16
ts-node: 10.7.0_bvx74sey2cc4ezqwnfhit2yjju
tsup: 5.12.6_jzlwqgkjzt5mccsp6kc6j2iwe4
@ -247,7 +253,6 @@ packages:
dependencies:
'@jridgewell/gen-mapping': 0.1.1
'@jridgewell/trace-mapping': 0.3.13
dev: true
/@babel/code-frame/7.14.5:
resolution: {integrity: sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==}
@ -329,11 +334,10 @@ packages:
convert-source-map: 1.8.0
debug: 4.3.4
gensync: 1.0.0-beta.2
json5: 2.2.0
json5: 2.2.1
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/eslint-parser/7.17.0_ei34yuzoyih6t7d227j2sk6bc4:
resolution: {integrity: sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==}
@ -364,7 +368,6 @@ packages:
'@babel/types': 7.18.4
'@jridgewell/gen-mapping': 0.3.1
jsesc: 2.5.2
dev: true
/@babel/helper-annotate-as-pure/7.16.7:
resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==}
@ -429,7 +432,6 @@ packages:
'@babel/helper-validator-option': 7.16.7
browserslist: 4.20.3
semver: 6.3.0
dev: true
/@babel/helper-create-class-features-plugin/7.18.0:
resolution: {integrity: sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==}
@ -549,7 +551,6 @@ packages:
/@babel/helper-environment-visitor/7.18.2:
resolution: {integrity: sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-explode-assignable-expression/7.16.7:
resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==}
@ -613,7 +614,6 @@ packages:
'@babel/types': 7.18.4
transitivePeerDependencies:
- supports-color
dev: true
/@babel/helper-optimise-call-expression/7.16.7:
resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==}
@ -626,11 +626,6 @@ packages:
resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==}
dev: true
/@babel/helper-plugin-utils/7.14.5:
resolution: {integrity: sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-plugin-utils/7.16.7:
resolution: {integrity: sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==}
engines: {node: '>=6.9.0'}
@ -638,7 +633,6 @@ packages:
/@babel/helper-plugin-utils/7.17.12:
resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-remap-async-to-generator/7.16.8:
resolution: {integrity: sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==}
@ -675,7 +669,6 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.4
dev: true
/@babel/helper-skip-transparent-expression-wrappers/7.16.0:
resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==}
@ -693,6 +686,7 @@ packages:
/@babel/helper-validator-identifier/7.15.7:
resolution: {integrity: sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier/7.16.7:
resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
@ -733,7 +727,6 @@ packages:
'@babel/types': 7.18.4
transitivePeerDependencies:
- supports-color
dev: true
/@babel/highlight/7.14.5:
resolution: {integrity: sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==}
@ -1214,7 +1207,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.17.4:
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
@ -1231,7 +1224,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.14.5
'@babel/helper-plugin-utils': 7.17.12
dev: false
/@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.17.4:
@ -1257,7 +1250,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.17.4:
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
@ -1385,7 +1378,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.14.5
'@babel/helper-plugin-utils': 7.17.12
dev: false
/@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.17.4:
@ -1411,7 +1404,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.17.4:
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
@ -1474,7 +1467,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.17.4:
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
@ -1499,7 +1492,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.17.4:
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
@ -1524,7 +1517,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.17.4:
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
@ -1558,7 +1551,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.17.4:
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
@ -1583,7 +1576,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.17.4:
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
@ -1608,7 +1601,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.17.4:
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
@ -1654,7 +1647,7 @@ packages:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.15.5
'@babel/helper-plugin-utils': 7.16.7
'@babel/helper-plugin-utils': 7.17.12
/@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.17.4:
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
@ -2937,7 +2930,6 @@ packages:
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types/7.15.6:
resolution: {integrity: sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==}
@ -2945,6 +2937,7 @@ packages:
dependencies:
'@babel/helper-validator-identifier': 7.15.7
to-fast-properties: 2.0.0
dev: false
/@babel/types/7.17.10:
resolution: {integrity: sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==}
@ -3226,6 +3219,49 @@ packages:
- ts-node
dev: true
/@jest/core/28.1.1_ts-node@10.7.0:
resolution: {integrity: sha512-3pYsBoZZ42tXMdlcFeCc/0j9kOlK7MYuXs2B1QbvDgMoW1K9NJ4G/VYvIbMb26iqlkTfPHo7SC2JgjDOk/mxXw==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
dependencies:
'@jest/console': 28.1.1
'@jest/reporters': 28.1.1
'@jest/test-result': 28.1.1
'@jest/transform': 28.1.1
'@jest/types': 28.1.1
'@types/node': 16.11.33
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.3.1
exit: 0.1.2
graceful-fs: 4.2.10
jest-changed-files: 28.0.2
jest-config: 28.1.1_p2brwx6nx5zpemlvbnwk444zqy
jest-haste-map: 28.1.1
jest-message-util: 28.1.1
jest-regex-util: 28.0.2
jest-resolve: 28.1.1
jest-resolve-dependencies: 28.1.1
jest-runner: 28.1.1
jest-runtime: 28.1.1
jest-snapshot: 28.1.1
jest-util: 28.1.1
jest-validate: 28.1.1
jest-watcher: 28.1.1
micromatch: 4.0.5
pretty-format: 28.1.1
rimraf: 3.0.2
slash: 3.0.0
strip-ansi: 6.0.1
transitivePeerDependencies:
- supports-color
- ts-node
dev: true
/@jest/create-cache-key-function/27.5.1:
resolution: {integrity: sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@ -3302,7 +3338,7 @@ packages:
chalk: 4.1.2
collect-v8-coverage: 1.0.1
exit: 0.1.2
glob: 7.2.0
glob: 7.2.3
graceful-fs: 4.2.10
istanbul-lib-coverage: 3.2.0
istanbul-lib-instrument: 5.2.0
@ -3361,9 +3397,9 @@ packages:
resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==}
engines: {node: '>= 10.14.2'}
dependencies:
'@babel/core': 7.15.5
'@babel/core': 7.17.4
'@jest/types': 26.6.2
babel-plugin-istanbul: 6.0.0
babel-plugin-istanbul: 6.1.1
chalk: 4.1.2
convert-source-map: 1.8.0
fast-json-stable-stringify: 2.1.0
@ -3371,7 +3407,7 @@ packages:
jest-haste-map: 26.6.2
jest-regex-util: 26.0.0
jest-util: 26.6.2
micromatch: 4.0.4
micromatch: 4.0.5
pirates: 4.0.5
slash: 3.0.0
source-map: 0.6.1
@ -3394,7 +3430,7 @@ packages:
jest-haste-map: 28.1.1
jest-regex-util: 28.0.2
jest-util: 28.1.1
micromatch: 4.0.4
micromatch: 4.0.5
pirates: 4.0.5
slash: 3.0.0
write-file-atomic: 4.0.1
@ -3449,12 +3485,10 @@ packages:
'@jridgewell/set-array': 1.1.0
'@jridgewell/sourcemap-codec': 1.4.12
'@jridgewell/trace-mapping': 0.3.13
dev: true
/@jridgewell/resolve-uri/3.0.7:
resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/set-array/1.1.0:
resolution: {integrity: sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==}
@ -3475,7 +3509,6 @@ packages:
dependencies:
'@jridgewell/resolve-uri': 3.0.7
'@jridgewell/sourcemap-codec': 1.4.12
dev: true
/@mapbox/node-pre-gyp/1.0.5:
resolution: {integrity: sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==}
@ -5833,7 +5866,7 @@ packages:
eslint: 8.17.0
eslint-config-galex: 3.6.5_eslint@8.17.0+jest@28.1.1
eslint-plugin-es: 4.1.0_eslint@8.17.0
jest: 28.1.1_@types+node@16.11.33
jest: 28.1.1_ljamerpmo44qvadngd6o6tmh2u
transitivePeerDependencies:
- supports-color
dev: true
@ -5921,7 +5954,7 @@ packages:
/@types/babel__traverse/7.14.2:
resolution: {integrity: sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==}
dependencies:
'@babel/types': 7.15.6
'@babel/types': 7.18.4
/@types/bcrypt/5.0.0:
resolution: {integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==}
@ -7363,7 +7396,7 @@ packages:
babel-plugin-istanbul: 6.0.0
babel-preset-jest: 26.6.2_@babel+core@7.15.5
chalk: 4.1.2
graceful-fs: 4.2.8
graceful-fs: 4.2.10
slash: 3.0.0
transitivePeerDependencies:
- supports-color
@ -7466,6 +7499,7 @@ packages:
test-exclude: 6.0.0
transitivePeerDependencies:
- supports-color
dev: false
/babel-plugin-istanbul/6.1.1:
resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
@ -7478,7 +7512,6 @@ packages:
test-exclude: 6.0.0
transitivePeerDependencies:
- supports-color
dev: true
/babel-plugin-jest-hoist/26.6.2:
resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==}
@ -9749,14 +9782,14 @@ packages:
dependencies:
find-root: 1.1.0
glob-parent: 6.0.2
resolve: 1.22.0
resolve: 1.22.1
dev: true
/eslint-import-resolver-node/0.3.6:
resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
dependencies:
debug: 3.2.7
resolve: 1.22.0
resolve: 1.22.1
dev: true
/eslint-module-utils/2.7.3:
@ -9796,7 +9829,7 @@ packages:
is-glob: 4.0.3
minimatch: 3.1.2
object.values: 1.1.5
resolve: 1.22.0
resolve: 1.22.1
tsconfig-paths: 3.14.1
dev: true
@ -9837,7 +9870,7 @@ packages:
'@typescript-eslint/eslint-plugin': 5.12.0_aq3u4h7brb5mlv46e3ysvg7bse
'@typescript-eslint/utils': 5.28.0_l7pgrp53lxkdqes4ltx44plfmm
eslint: 8.17.0
jest: 28.1.1_@types+node@16.11.33
jest: 28.1.1_ljamerpmo44qvadngd6o6tmh2u
transitivePeerDependencies:
- supports-color
- typescript
@ -10207,7 +10240,7 @@ packages:
is-stream: 1.1.0
npm-run-path: 2.0.2
p-finally: 1.0.0
signal-exit: 3.0.4
signal-exit: 3.0.7
strip-eof: 1.0.0
/execa/5.1.1:
@ -11079,6 +11112,7 @@ packages:
minimatch: 3.1.2
once: 1.4.0
path-is-absolute: 1.0.1
dev: true
/glob/7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
@ -11179,10 +11213,6 @@ packages:
/graceful-fs/4.2.10:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
/graceful-fs/4.2.8:
resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==}
dev: false
/handlebars/4.7.7:
resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
engines: {node: '>=0.4.7'}
@ -12178,12 +12208,13 @@ packages:
resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==}
engines: {node: '>=8'}
dependencies:
'@babel/core': 7.15.5
'@babel/core': 7.17.4
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.0
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: false
/istanbul-lib-instrument/5.2.0:
resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==}
@ -12196,7 +12227,6 @@ packages:
semver: 6.3.0
transitivePeerDependencies:
- supports-color
dev: true
/istanbul-lib-report/3.0.0:
resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
@ -12313,6 +12343,34 @@ packages:
- ts-node
dev: true
/jest-cli/28.1.1_ljamerpmo44qvadngd6o6tmh2u:
resolution: {integrity: sha512-+sUfVbJqb1OjBZ0OdBbI6OWfYM1i7bSfzYy6gze1F1w3OKWq8ZTEKkZ8a7ZQPq6G/G1qMh/uKqpdWhgl11NFQQ==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
dependencies:
'@jest/core': 28.1.1_ts-node@10.7.0
'@jest/test-result': 28.1.1
'@jest/types': 28.1.1
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.10
import-local: 3.1.0
jest-config: 28.1.1_ljamerpmo44qvadngd6o6tmh2u
jest-util: 28.1.1
jest-validate: 28.1.1
prompts: 2.4.2
yargs: 17.5.1
transitivePeerDependencies:
- '@types/node'
- supports-color
- ts-node
dev: true
/jest-config/28.1.1_@types+node@16.11.33:
resolution: {integrity: sha512-tASynMhS+jVV85zKvjfbJ8nUyJS/jUSYZ5KQxLUN2ZCvcQc/OmhQl2j6VEL3ezQkNofxn5pQ3SPYWPHb0unTZA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -12352,6 +12410,86 @@ packages:
- supports-color
dev: true
/jest-config/28.1.1_ljamerpmo44qvadngd6o6tmh2u:
resolution: {integrity: sha512-tASynMhS+jVV85zKvjfbJ8nUyJS/jUSYZ5KQxLUN2ZCvcQc/OmhQl2j6VEL3ezQkNofxn5pQ3SPYWPHb0unTZA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
peerDependenciesMeta:
'@types/node':
optional: true
ts-node:
optional: true
dependencies:
'@babel/core': 7.17.4
'@jest/test-sequencer': 28.1.1
'@jest/types': 28.1.1
'@types/node': 16.10.1
babel-jest: 28.1.1_@babel+core@7.17.4
chalk: 4.1.2
ci-info: 3.3.1
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.10
jest-circus: 28.1.1
jest-environment-node: 28.1.1
jest-get-type: 28.0.2
jest-regex-util: 28.0.2
jest-resolve: 28.1.1
jest-runner: 28.1.1
jest-util: 28.1.1
jest-validate: 28.1.1
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 28.1.1
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.7.0_bvx74sey2cc4ezqwnfhit2yjju
transitivePeerDependencies:
- supports-color
dev: true
/jest-config/28.1.1_p2brwx6nx5zpemlvbnwk444zqy:
resolution: {integrity: sha512-tASynMhS+jVV85zKvjfbJ8nUyJS/jUSYZ5KQxLUN2ZCvcQc/OmhQl2j6VEL3ezQkNofxn5pQ3SPYWPHb0unTZA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
peerDependencies:
'@types/node': '*'
ts-node: '>=9.0.0'
peerDependenciesMeta:
'@types/node':
optional: true
ts-node:
optional: true
dependencies:
'@babel/core': 7.17.4
'@jest/test-sequencer': 28.1.1
'@jest/types': 28.1.1
'@types/node': 16.11.33
babel-jest: 28.1.1_@babel+core@7.17.4
chalk: 4.1.2
ci-info: 3.3.1
deepmerge: 4.2.2
glob: 7.2.3
graceful-fs: 4.2.10
jest-circus: 28.1.1
jest-environment-node: 28.1.1
jest-get-type: 28.0.2
jest-regex-util: 28.0.2
jest-resolve: 28.1.1
jest-runner: 28.1.1
jest-util: 28.1.1
jest-validate: 28.1.1
micromatch: 4.0.5
parse-json: 5.2.0
pretty-format: 28.1.1
slash: 3.0.0
strip-json-comments: 3.1.1
ts-node: 10.7.0_bvx74sey2cc4ezqwnfhit2yjju
transitivePeerDependencies:
- supports-color
dev: true
/jest-diff/27.5.1:
resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@ -12426,9 +12564,9 @@ packages:
jest-serializer: 26.6.2
jest-util: 26.6.2
jest-worker: 26.6.2
micromatch: 4.0.4
micromatch: 4.0.5
sane: 4.1.0
walker: 1.0.7
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.2
@ -12445,7 +12583,7 @@ packages:
jest-regex-util: 28.0.2
jest-util: 28.1.1
jest-worker: 28.1.1
micromatch: 4.0.4
micromatch: 4.0.5
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.2
@ -12488,7 +12626,7 @@ packages:
'@types/stack-utils': 2.0.1
chalk: 4.1.2
graceful-fs: 4.2.10
micromatch: 4.0.4
micromatch: 4.0.5
pretty-format: 28.1.1
slash: 3.0.0
stack-utils: 2.0.5
@ -12551,7 +12689,7 @@ packages:
jest-pnp-resolver: 1.2.2_jest-resolve@28.1.1
jest-util: 28.1.1
jest-validate: 28.1.1
resolve: 1.22.0
resolve: 1.22.1
resolve.exports: 1.1.0
slash: 3.0.0
dev: true
@ -12600,7 +12738,7 @@ packages:
cjs-module-lexer: 1.2.2
collect-v8-coverage: 1.0.1
execa: 5.1.1
glob: 7.2.0
glob: 7.2.3
graceful-fs: 4.2.10
jest-haste-map: 28.1.1
jest-message-util: 28.1.1
@ -12662,7 +12800,7 @@ packages:
chalk: 4.1.2
graceful-fs: 4.2.10
is-ci: 2.0.0
micromatch: 4.0.4
micromatch: 4.0.5
/jest-util/28.1.1:
resolution: {integrity: sha512-FktOu7ca1DZSyhPAxgxB6hfh2+9zMoJ7aEQA759Z6p45NuO8mWcqujH+UdHlCm/V6JTWwDztM2ITCzU1ijJAfw==}
@ -12673,7 +12811,7 @@ packages:
chalk: 4.1.2
ci-info: 3.3.1
graceful-fs: 4.2.10
picomatch: 2.3.0
picomatch: 2.3.1
dev: true
/jest-validate/28.1.1:
@ -12748,6 +12886,26 @@ packages:
- ts-node
dev: true
/jest/28.1.1_ljamerpmo44qvadngd6o6tmh2u:
resolution: {integrity: sha512-qw9YHBnjt6TCbIDMPMpJZqf9E12rh6869iZaN08/vpOGgHJSAaLLUn6H8W3IAEuy34Ls3rct064mZLETkxJ2XA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
hasBin: true
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
dependencies:
'@jest/core': 28.1.1_ts-node@10.7.0
'@jest/types': 28.1.1
import-local: 3.1.0
jest-cli: 28.1.1_ljamerpmo44qvadngd6o6tmh2u
transitivePeerDependencies:
- '@types/node'
- supports-color
- ts-node
dev: true
/joycon/3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
@ -12856,7 +13014,6 @@ packages:
resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
engines: {node: '>=6'}
hasBin: true
dev: true
/jsonfile/6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
@ -13318,16 +13475,10 @@ packages:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/makeerror/1.0.11:
resolution: {integrity: sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=}
dependencies:
tmpl: 1.0.5
/makeerror/1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
dependencies:
tmpl: 1.0.5
dev: true
/map-age-cleaner/0.1.3:
resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==}
@ -13880,6 +14031,7 @@ packages:
dependencies:
braces: 3.0.2
picomatch: 2.3.0
dev: true
/micromatch/4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
@ -14288,7 +14440,7 @@ packages:
whatwg-url: 5.0.0
/node-int64/0.4.0:
resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=}
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
/node-libs-browser/2.2.1:
resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==}
@ -14363,7 +14515,7 @@ packages:
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
dependencies:
hosted-git-info: 2.8.9
resolve: 1.22.0
resolve: 1.22.1
semver: 5.7.1
validate-npm-package-license: 3.0.4
dev: true
@ -16331,7 +16483,7 @@ packages:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
hasBin: true
dependencies:
glob: 7.2.0
glob: 7.2.3
/ripemd160/2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
@ -16421,7 +16573,7 @@ packages:
fb-watchman: 2.0.1
micromatch: 3.1.10
minimist: 1.2.6
walker: 1.0.7
walker: 1.0.8
/scheduler/0.22.0:
resolution: {integrity: sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==}
@ -17409,7 +17561,7 @@ packages:
engines: {node: '>=8'}
dependencies:
'@istanbuljs/schema': 0.1.3
glob: 7.2.0
glob: 7.2.3
minimatch: 3.1.2
/text-decoding/1.0.0:
@ -17417,7 +17569,7 @@ packages:
dev: false
/text-table/0.2.0:
resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=}
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
/textextensions/5.14.0:
@ -18269,16 +18421,10 @@ packages:
resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
dev: true
/walker/1.0.7:
resolution: {integrity: sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=}
dependencies:
makeerror: 1.0.11
/walker/1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
dependencies:
makeerror: 1.0.12
dev: true
/watchpack-chokidar2/2.0.1:
resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==}