diff --git a/.env b/.env index e69de29..4c79873 100644 --- a/.env +++ b/.env @@ -0,0 +1,14 @@ +# Seeker socket path +IPC_SOCKET_PATH = /tmp/seeker.sock + +# Database ----------------------------------------------------------------------------------------- + +DB_TYPE = mysql +DB_HOST = database +DB_PORT = 3306 +DB_USER = root +DB_PASSWORD_FILE = /run/secrets/mysql_root_password +DB_DATABASE = autoplex_request + +# Torrent client IPC socket path +TORRENT_CLIENT_IPC_SOCKET = /tmp/torrent_client.sock diff --git a/.env.example b/.env.example index e69de29..4c79873 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,14 @@ +# Seeker socket path +IPC_SOCKET_PATH = /tmp/seeker.sock + +# Database ----------------------------------------------------------------------------------------- + +DB_TYPE = mysql +DB_HOST = database +DB_PORT = 3306 +DB_USER = root +DB_PASSWORD_FILE = /run/secrets/mysql_root_password +DB_DATABASE = autoplex_request + +# Torrent client IPC socket path +TORRENT_CLIENT_IPC_SOCKET = /tmp/torrent_client.sock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71066f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock +.data/ + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/nodemon.json b/nodemon.json index be28bf3..ab65884 100644 --- a/nodemon.json +++ b/nodemon.json @@ -2,7 +2,7 @@ "watch": ["src"], "ext": "ts,json", "ignore": ["src/**/*.spec.ts"], - "exec": "node --inspect=0.0.0.0:9230 -r tsconfig-paths/register -r ts-node/register src/index.ts", + "exec": "node --inspect=0.0.0.0:9230 -r ts-node/register src/index.ts", "events": { "start": "clear" } diff --git a/package.json b/package.json index 1eed213..5d6685b 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,18 @@ "start:dev": "nodemon" }, "devDependencies": { + "@types/node": "^14.14.41", + "@types/xml2js": "^0.4.8", + "nodemon": "^2.0.7", + "rimraf": "^3.0.2", "ts-node": "^9.1.1", "typescript": "^4.2.4" }, "dependencies": { - "typeorm": "^0.2.32" + "cheerio": "^1.0.0-rc.6", + "mysql": "^2.18.1", + "node-ipc": "^9.1.4", + "typeorm": "^0.2.32", + "xml2js": "^0.4.23" } } diff --git a/src/Application.ts b/src/Application.ts new file mode 100644 index 0000000..4e71899 --- /dev/null +++ b/src/Application.ts @@ -0,0 +1,104 @@ +import services from "./services"; +import Service from "./services/Service"; +import assert from "assert"; + +interface ServiceMap { + [name: string]: Service +} + +/** + * The main application class + */ +export default class Application +{ + private static __instance: Application; + + /** + * All available services + */ + protected services: ServiceMap = {}; + + /** + * Return the current application instance + */ + public static instance() { + return this.__instance; + } + + /** + * Create a new application instance + */ + public constructor() { + Application.__instance = this; + for (let ServiceClass of Object.values(services)) { + this.installService(ServiceClass); + } + } + + /** + * Install a service into the application + */ + protected installService(ServiceClass: new (app: Application) => Service) { + let service = new ServiceClass(this); + this.services[service.name] = service; + } + + /** + * Boot the application and all of the services + */ + protected async boot() { + let services = Object.values(this.services); + return Promise.all(services.map(service => service.boot())); + } + + /** + * Initialize the application if necessary + */ + protected async initialize() { + + } + + /** + * Shutdown the application + */ + protected shutdown() { + let services = Object.values(this.services); + return Promise.all(services.map(service => service.shutdown())); + } + + /** + * Start the application + */ + public async start() { + await this.boot(); + await this.initialize(); + for (let service of Object.values(this.services)) { + service.start(); + } + } + + /** + * Quit the application + */ + public async quit(code: number = 0) { + await this.shutdown(); + process.exit(code); + } + + // Access -------------------------------------------------------------------------------------- + + /** + * Get all available services + */ + public serviceList() { + return Object.keys(this.services); + } + + /** + * Get an application service instance + */ + public service(serviceName: string) { + assert(serviceName in this.services); + return this.services[serviceName]; + } +} diff --git a/src/database/entities/MovieInfo.ts b/src/database/entities/MovieInfo.ts new file mode 100644 index 0000000..4f4f5f3 --- /dev/null +++ b/src/database/entities/MovieInfo.ts @@ -0,0 +1,26 @@ +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"; + +@Entity() +export class MovieInfo extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column({ unique: true }) + tmdbId!: number; + + @Column({ type: "text", nullable: true }) + overview!: string | null; + + @Column({ type: "int", nullable: true }) + runtime!: number | null; + + @Column({ type: "char", length: 10, nullable: true }) + releaseDate!: string | null; + + @Column({ type: "varchar", length: 32, nullable: true }) + backdropPath!: string | null; + + @Column({ type: "varchar", length: 32, nullable: true }) + posterPath!: string | null; +} diff --git a/src/database/entities/MovieQuota.ts b/src/database/entities/MovieQuota.ts new file mode 100644 index 0000000..edba67b --- /dev/null +++ b/src/database/entities/MovieQuota.ts @@ -0,0 +1,11 @@ +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; + +@Entity() +export class MovieQuota extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column({ default: 5 }) + moviesPerWeek!: number; +} diff --git a/src/database/entities/MovieTicket.ts b/src/database/entities/MovieTicket.ts new file mode 100644 index 0000000..3e96a60 --- /dev/null +++ b/src/database/entities/MovieTicket.ts @@ -0,0 +1,42 @@ +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, ManyToOne, OneToMany, OneToOne, JoinColumn, CreateDateColumn } from "typeorm"; +import { MovieInfo } from "./MovieInfo"; +import { MovieTorrent } from "./MovieTorrent"; +import { User } from "./User"; + +@Entity() +export class MovieTicket extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column({ type: "varchar", length: 27, nullable: true }) + imdbId!: string | null; + + @Column({ type: "varchar" }) + title!: string; + + @Column({ type: "year", nullable: true }) + year!: number | null; + + @CreateDateColumn() + createdAt!: Date; + + @Column({ default: false }) + isFulfilled!: boolean; + + @Column({ default: false }) + isCanceled!: boolean; + + @Column({ default: false }) + isStale!: boolean; + + @ManyToOne(() => User, user => user.movieTickets) + user!: User; + + @OneToMany(() => MovieTorrent, torrent => torrent.movieTicket) + torrents!: MovieTorrent[]; + + @OneToOne(() => MovieInfo, { nullable: true }) + @JoinColumn() + info!: MovieInfo | null; +} diff --git a/src/database/entities/MovieTorrent.ts b/src/database/entities/MovieTorrent.ts new file mode 100644 index 0000000..8743acf --- /dev/null +++ b/src/database/entities/MovieTorrent.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, ManyToOne } from "typeorm"; +import { MovieTicket } from "./MovieTicket"; + +@Entity() +export class MovieTorrent extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column() + infoHash!: string; + + @ManyToOne(() => MovieTicket, ticket => ticket.torrents) + movieTicket!: MovieTicket; +} diff --git a/src/database/entities/RegisterToken.ts b/src/database/entities/RegisterToken.ts new file mode 100644 index 0000000..3fe2c55 --- /dev/null +++ b/src/database/entities/RegisterToken.ts @@ -0,0 +1,39 @@ +import { randomBytes } from "crypto"; +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"; + +@Entity() +export class RegisterToken extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column() + token!: string + + /** + * Check if the provided token is valid + */ + public static async isValid(token: string) { + if (typeof token !== "string") { + return false; + } + return Boolean(token) && await RegisterToken.count({token}) > 0; + } + + /** + * Create a new registration token and insert it into the database + */ + public static generate() { + return new Promise((resolve, reject) => { + randomBytes(48, async (err, result) => { + if (err) { + reject(err); + } else { + let token = new RegisterToken(); + token.token = result.toString("hex"); + resolve(await token.save()); + } + }); + }); + } +} diff --git a/src/database/entities/User.ts b/src/database/entities/User.ts new file mode 100644 index 0000000..07150b2 --- /dev/null +++ b/src/database/entities/User.ts @@ -0,0 +1,32 @@ +import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, OneToMany, OneToOne, JoinColumn, CreateDateColumn } from "typeorm"; +import { MovieTicket } from "./MovieTicket"; +import { MovieQuota } from "./MovieQuota"; + +@Entity() +export class User extends BaseEntity +{ + @PrimaryGeneratedColumn() + id!: number; + + @Column() + isAdmin!: boolean; + + @Column({ length: 50 }) + name!: string; + + @Column({ length: 255 }) + email!: string; + + @Column({ type: "char", length: 60 }) + password!: string; + + @CreateDateColumn() + createdAt!: Date; + + @OneToOne(() => MovieQuota, { nullable: true }) + @JoinColumn() + quota!: MovieQuota; + + @OneToMany(() => User, user => user.movieTickets) + movieTickets!: MovieTicket[]; +} diff --git a/src/database/entities/index.ts b/src/database/entities/index.ts new file mode 100644 index 0000000..2b8c4c9 --- /dev/null +++ b/src/database/entities/index.ts @@ -0,0 +1,6 @@ +export * from "./MovieInfo"; +export * from "./MovieQuota"; +export * from "./MovieTicket"; +export * from "./MovieTorrent"; +export * from "./RegisterToken"; +export * from "./User"; diff --git a/src/index.ts b/src/index.ts index e69de29..d5d625d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,7 @@ +import Application from "./Application"; + +// Create a new application instance +let app = new Application(); + +// Start the application +app.start(); diff --git a/src/services/Database.ts b/src/services/Database.ts new file mode 100644 index 0000000..79a3231 --- /dev/null +++ b/src/services/Database.ts @@ -0,0 +1,49 @@ +import { Connection, createConnection } from "typeorm"; +import * as entities from "../database/entities"; +import Service from "./Service"; +import { readFile } from "fs/promises"; +import Application from "../Application"; + +export default class Database extends Service +{ + /** + * The active database connection + */ + protected connection!: Connection; + + /** + * Create a new database instance + */ + public constructor(app: Application) { + super("Database", app); + } + + /** + * Boot the database service + */ + public async boot() { + // Fetch the database password from the secret file + let password = (await readFile(process.env["DB_PASSWORD_FILE"])).toString().trim(); + + // Create the database connection + await createConnection({ + type : <"mysql" | "mariadb">process.env["DB_TYPE"], + host : process.env["DB_HOST"], + port : parseInt(process.env["DB_PORT"]), + username : process.env["DB_USER"], + password : password, + database : process.env["DB_DATABASE"], + // synchronize: process.env["NODE_ENV"] != "production", + synchronize: false, + entities : Object.values(entities), + migrations : ["src/migrations/*.ts"] + }); + } + + /** + * Shutdown the database service + */ + public async shutdown() { + await this.connection.close(); + } +} diff --git a/src/services/IpcInterface.ts b/src/services/IpcInterface.ts new file mode 100644 index 0000000..442ea25 --- /dev/null +++ b/src/services/IpcInterface.ts @@ -0,0 +1,85 @@ +import ipc from "node-ipc"; +import type { Server } from "node-ipc"; +import { Socket } from "net"; +import Service from "./Service"; +import Application from "../Application"; +import MovieSearch from "./MovieSearch"; +import Supervisor from "./Supervisor"; +import { MovieTicket } from "../database/entities"; + +export default class IpcInterface extends Service +{ + /** + * Quick reference to the IPC server + */ + protected server!: Server; + + /** + * Create a new IPC interface + */ + public constructor(app: Application) { + super("IPC", app); + ipc.config.id = "seeker"; + ipc.config.retry = 1500; + ipc.config.silent = true; + } + + /** + * Boot the IPC interface + */ + public boot() { + return new Promise((resolve, reject) => { + console.log("Serving:", process.env["IPC_SOCKET_PATH"]); + ipc.serve(process.env["IPC_SOCKET_PATH"], () => { + this.server = ipc.server; + this.installEventHandlers(this.server); + resolve(); + }); + ipc.server.start(); + }); + } + + public async shutdown() { + if (this.server) { + this.server.stop(); + } + } + + /** + * Install the the event handlers + */ + protected installEventHandlers(server: Server) { + this.addEventHandler(server, "search_movie", this.searchMovie); + } + + /** + * Handle a specific event + */ + protected addEventHandler(server: Server, method: string, handle: (...args: any[]) => Promise) { + server.on(method, async (message: any, socket: Socket) => { + try { + let response = await handle.apply(this, [message]); + this.server.emit(socket, method, { response }); + } catch (error) { + console.log("Error:", method, error); + this.server.emit(socket, method, { + response: undefined, + error + }); + } + }); + } + + // Interface Methods --------------------------------------------------------------------------- + + /** + * Invoked when a new Movie ticket has been created + */ + protected async searchMovie(ticketId: number) { + let movie = await MovieTicket.findOne(ticketId); + if (movie === undefined) { + return null; + } + this.app.service("Supervisor").searchMovie(movie); + } +} diff --git a/src/services/MovieSearch.ts b/src/services/MovieSearch.ts new file mode 100644 index 0000000..323f712 --- /dev/null +++ b/src/services/MovieSearch.ts @@ -0,0 +1,75 @@ +import Application from "../Application"; +import { MovieTicket } from "../database/entities"; +import * as providerClasses from "../torrents"; +import Provider, { MediaType } from "../torrents/providers/Provider"; +import Torrent from "../torrents/Torrent"; +import { rankTorrents } from "../torrents/ranking"; +import Service from "./Service"; + +export default class MovieSearch extends Service +{ + /** + * Available providers that support movies + */ + protected providers!: Provider[]; + + /** + * Create a new instance of the movie search service + */ + public constructor(app: Application) { + super("Movie Search", app); + } + + /** + * Boot the movie search service + */ + public async boot() { + let providers = Object.values(providerClasses); + this.providers = providers.filter(provider => provider.PROVIDES & MediaType.Movies) + .map(ProviderClass => new ProviderClass()); + } + + /** + * Shutdown the service + */ + public async shutdown() { + + } + + /** + * Search for a movie + */ + public async searchMovie(movie: MovieTicket) { + // Search by IMDb + let torrents = await this.searchImdb(movie); + if (torrents.length == 0) { + return null; + } + // Determine the preferred torrents + let preferredTorrents = rankTorrents(torrents); + if (preferredTorrents.length == 0) { + preferredTorrents = torrents; + } + // Return the selected torrent + this.log("Found movie torrent for", movie.title); + return await preferredTorrents[0].downloadLink(); + } + + /** + * Search for a movie by its IMDb ID + */ + protected async searchImdb(movie: MovieTicket): Promise { + if (movie.imdbId == null) { + return []; + } + let results = await Promise.all(this.providers.map(provider => provider.searchMovie(movie))); + return ([]).concat(...results); + } + + /** + * Pick the best torrent from the list + */ + protected pickBestTorrent() { + + } +} diff --git a/src/services/Service.ts b/src/services/Service.ts new file mode 100644 index 0000000..61c0d2a --- /dev/null +++ b/src/services/Service.ts @@ -0,0 +1,58 @@ +import Application from "../Application"; + +export default abstract class Service +{ + /** + * The name of the service + */ + public readonly name: string; + + /** + * The application instance + */ + protected readonly app: Application; + + /** + * Enable/disable logging for this service + */ + public logging: boolean = true; + + /** + * Create a new service + */ + public constructor(name: string, app: Application) { + this.app = app; + this.name = name; + } + + // Required Service Implementation ------------------------------------------------------------- + + /** + * Boot the service + */ + public abstract boot(): Promise; + + /** + * Shut the application down + */ + public abstract shutdown(): Promise; + + // Miscellaneous ------------------------------------------------------------------------------ + + /** + * Indicate the application is ready + */ + public start() { + // no-op + }; + + /** + * Service-specific logging + */ + public log(...args: any[]) { + if (!this.logging) { + return; + } + console.log(`[${this.name}]:`, ...args); + } +} diff --git a/src/services/Supervisor.ts b/src/services/Supervisor.ts new file mode 100644 index 0000000..e959d08 --- /dev/null +++ b/src/services/Supervisor.ts @@ -0,0 +1,97 @@ +import Application from "../Application"; +import { MovieTicket, MovieTorrent } from "../database/entities"; +import MovieSearch from "./MovieSearch"; +import Service from "./Service"; +import TorrentClientIpc, { TorrentClientConnectionError } from "./TorrentClientIpc"; + +export default class Supervisor extends Service +{ + /** + * Keep a list of pending torrent links to add + */ + protected pendingTorrentsToAdd: string[]; + + /** + * The movie search service instance + */ + protected movieSearch!: MovieSearch; + + /** + * The torrent client IPC service instance + */ + protected torrentClient!: TorrentClientIpc; + + /** + * Create a new supervisor service instance + */ + public constructor(app: Application) { + super("Supervisor", app); + this.pendingTorrentsToAdd = []; + } + + /** + * Boot the supervisor service + */ + public async boot() {} + + /** + * All services are booted and ready + */ + public start() { + this.movieSearch = this.app.service("Movie Search"); + this.torrentClient = this.app.service("Torrent Client IPC"); + this.searchMovies(); + } + + /** + * Shutdown the supervisor service + */ + public async shutdown() {} + + // Tasks --------------------------------------------------------------------------------------- + + /** + * @TODO Performing a promise-all instead of waiting between each movie may be much faster + * Search available movies in the database + */ + public async searchMovies() { + let movies = await MovieTicket.find({where: {isFulfilled: false}, relations: [ "torrents" ]}); + for (let movie of movies) { + // Skip already-resolved non-stale torrents + if (movie.torrents.length > 0 && !movie.isStale) { + this.log("Skipping already satisfied ticket") + continue; + } + await this.searchMovie(movie); + } + } + + /** + * Search for a movie and add it to the torrent client + */ + public async searchMovie(movie: MovieTicket) { + // Search for a movie torrent + let link = await this.movieSearch.searchMovie(movie); + if (link === null) { + return false; + } + this.log("Found a torrent for:", movie.title, link); + // Send the link to the client + let infoHash: string; + try { + infoHash = await this.torrentClient.add(link); + } catch(e) { + if (e instanceof TorrentClientConnectionError) { + this.log("Failed to add torrent to client... Added to pending"); + this.pendingTorrentsToAdd.push(link); + } + return false; + } + // Store a reference to this torrent in the database + let torrent = new MovieTorrent(); + torrent.infoHash = infoHash; + torrent.movieTicket = movie; + await torrent.save(); + return true; + } +} diff --git a/src/services/TorrentClientIpc.ts b/src/services/TorrentClientIpc.ts new file mode 100644 index 0000000..a366505 --- /dev/null +++ b/src/services/TorrentClientIpc.ts @@ -0,0 +1,221 @@ +import ipc from "node-ipc"; +import { Socket } from "net"; +import Application from "../Application"; +import Service from "./Service"; + +interface IResponse { + response?: any, + error?: string | Error +} + +export interface ITorrent { + name: string, + infoHash: string, + progress: number, + state: TorrentState +} + +export interface ISerializedFile { + path : string; + size : number; + downloaded: number; + progress : number; + selected : boolean; +} + +export interface ISerializedTorrent { + name : string; + infoHash : string; + downloaded : number; + uploaded : number; + ratio : number; + size : number; + downloadSpeed: number; + uploadSpeed : number; + numPeers : number; + progress : number; + path : string; + state : TorrentState; + files : ISerializedFile[]; +} + +export enum TorrentState { + Ready = 0x1, + Paused = 0x2, + Done = 0x4 +} + +/** + * A custom error type for torrent client connection errors + */ +export class TorrentClientConnectionError extends Error { + constructor(...args: any[]) { + super(...args); + Object.setPrototypeOf(this, TorrentClientConnectionError.prototype); + } +} + +/** + * The torrent client IPC service + */ +export default class TorrentClientIpc extends Service +{ + /** + * Indicate if there is an active connection to the IPC + */ + private __isConnected: boolean; + + /** + * The active IPC socket + */ + protected socket!: Socket; + + /** + * Create a new IPC client for the torrent client + */ + constructor(app: Application) { + super("Torrent Client IPC", app); + ipc.config.id = "torrent_webui"; + ipc.config.retry = 1500; + ipc.config.silent = true; + + this.__isConnected = false; + } + + /** + * Boot the torrent client IPC service + */ + public boot() { + return new Promise((resolve, reject) => { + ipc.connectTo("torrent_client", process.env["TORRENT_CLIENT_IPC_SOCKET"], () => { + this.socket = ipc.of["torrent_client"]; + this.installSocketEventHandlers(this.socket); + this.installSocketMessageHandlers(this.socket); + resolve(); + }); + }); + } + + /** + * Shutdown the service + */ + public async shutdown() { + + } + + /** + * Install the event handlers for the IPC socket + */ + protected installSocketEventHandlers(socket: Socket) { + socket.on("connect", () => this.onConnect()); + socket.on("error", (error: any) => this.onError(error)); + socket.on("disconnect", () => this.onDisconnect()); + socket.on("destroy", () => this.onDestroy()); + } + + protected installSocketMessageHandlers(socket: Socket) { + } + + // Socket Event Handlers ----------------------------------------------------------------------- + + protected onConnect() { + this.log("IPC: Connection established"); + this.__isConnected = true; + } + + protected onError(error: string | Error) { + if (this.__isConnected) { + this.log("IPC: Error occurred:", error); + } + } + + protected onDisconnect() { + if (this.__isConnected) { + this.log("IPC: Disconnected"); + } + this.__isConnected = false; + } + + protected onDestroy() { + this.log("IPC: Destroyed"); + } + + // Methods ------------------------------------------------------------------------------------- + + /** + * Perform a general request to the torrent client + */ + protected async request(method: string, message?: any) { + return new Promise((resolve, reject) => { + if (!this.isConnected) { + reject(new TorrentClientConnectionError("Not connected to torrent client")); + return; + } + let respond = (response: any) => { + clearTimeout(timeout); + resolve(response); + } + // Include timeout mechanism in the off chance something breaks + let timeout = setTimeout(() => { + this.socket.off(method, respond); + reject(new TorrentClientConnectionError("Torrent client IPC request timeout")); + }, 1000); + this.socket.once(method, respond); + this.socket.emit(method, message); + }); + } + + /** + * Add a torrent to the client + * @param torrent Magnet URI or file buffer + */ + public async add(torrent: string | Buffer) { + let response = await this.request("add", torrent); + if (response.error) { + throw new Error("Failed to add torrent"); + } + return response.response; + } + + /** + * Remove a torrent from the client + * @param torrent Torrent info hash + */ + public async remove(torrent: string) { + let response = await this.request("remove", torrent); + if (response.error) { + throw new Error("Failed to remove torrent"); + } + } + + /** + * Get a list of all torrents in the client + */ + public async list() { + let response = await this.request("list"); + if (response.error) { + console.error(response.error); + throw new Error("Failed to obtain torrent list"); + } + return response.response; + } + + /** + * Get full details of each of the provided torrents + * @param torrentIds Array of torrent info hashes + */ + public async details(...torrentIds: string[]) { + let response = await this.request("details", torrentIds); + if (response.error) { + console.error(response.error); + throw new Error("Failed to retrieve torrent details"); + } + return response.response; + } + + // Accessors ----------------------------------------------------------------------------------- + + get isConnected() { + return this.__isConnected; + } +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..0eede1e --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,13 @@ +import Database from "./Database"; +import IpcInterface from "./IpcInterface"; +import MovieSearch from "./MovieSearch"; +import Supervisor from "./Supervisor"; +import TorrentClientIpc from "./TorrentClientIpc"; + +export default { + Database, + IpcInterface, + MovieSearch, + Supervisor, + TorrentClientIpc, +} diff --git a/src/torrents/Torrent.ts b/src/torrents/Torrent.ts new file mode 100644 index 0000000..0ba82bb --- /dev/null +++ b/src/torrents/Torrent.ts @@ -0,0 +1,63 @@ +import { MovieTicket } from "../database/entities"; +import { ITorrentMetaInfo, parseMovieTorrentName } from "./parsing"; + +export default class Torrent +{ + /** + * The name of the torrent + */ + public readonly name: string; + + /** + * The size of the torrent in bytes (if available) + */ + public readonly size: number | null; + + /** + * The number of seeders (if available) + */ + public readonly seeders: number; + + /** + * Download link (if available) + */ + protected readonly link: string | null; + + /** + * Metadata of the torrent + */ + public readonly metadata: ITorrentMetaInfo; + + /** + * Create a new Torrent instance + * + * @param name The name of the torrent + * @param size The size of the torrent in bytes (if available) + * @param seeders The number of seeders (if available) + * @param link The number of seeders (if available) + */ + public constructor(movie: MovieTicket, name: string, size?: number, seeders?: number, link?: string) { + this.name = name.trim(); + this.size = size ?? null; + this.seeders = seeders ?? 1; + this.link = link ?? null; + this.metadata = parseMovieTorrentName(name, movie.title ?? "", movie.year ?? undefined); + } + + /** + * Return a link to download (magnet or .torrent) + */ + public async downloadLink() { + if (this.link === null) { + throw Error("Magnet link does not exist"); + } + return this.link; + } + + /** + * Serialize this torrent into a string + */ + public toString() { + return `Name: ${this.name}; Size: ${this.size}; Seeders: ${this.seeders};` + } +} diff --git a/src/torrents/index.ts b/src/torrents/index.ts new file mode 100644 index 0000000..da632a9 --- /dev/null +++ b/src/torrents/index.ts @@ -0,0 +1,5 @@ +import TorrentGalaxy from "./providers/torrentgalaxy"; + +export { + TorrentGalaxy +} diff --git a/src/torrents/parsing.ts b/src/torrents/parsing.ts new file mode 100644 index 0000000..80e40b5 --- /dev/null +++ b/src/torrents/parsing.ts @@ -0,0 +1,153 @@ +/** + * Video quality from lowest to highest + */ +export enum Resolution { + HD4k, + HD1080, + HD720, + SD384, + SD480, + SD360, + Unknown +} + +// https://en.wikipedia.org/wiki/Pirated_movie_release_types#DVD_and_VOD_ripping +// https://en.wikipedia.org/wiki/Standard_(warez)#cite_note-txd2k9-13 +/** + * Types of releases from lowest quality to highest + */ +export enum ReleaseType { + BluRay, + WebDl, + WebRip, + WebCap, + HDRip, + DVDR, + DVDRip, + Unknown, // Unknown is better than cam tbh + HDCAM, + CAM +} + +export enum VideoCodec { + XviD, + x264, + x265, +} + +export enum VideoCodecFlag { + REMUX, + HDR, + HEVC +} + +export enum AudioCodec { + AC3, + DD51, + AAC71, + Atmos71, + TenBit +} + +export interface ITorrentMetaInfo { + containsOtherLanguage: boolean, + resolution: Resolution, + releaseType: ReleaseType, +} + +/** + * Determine meta-info from a torrent name + */ +export function parseMovieTorrentName(torrentName: string, title: string = "", year?: number) { + // Split the meta info after the year if possible to make parsing more reliable + let split = torrentName.split(new RegExp(`${year}|\\(${year}\\)`)); + let metaInfo = split[split.length - 1]; + title = split.length > 1 ? "" : title; // No need to check title in parsing if split correctly + return { + containsOtherLanguage: determineIfContainsOtherLanguages(torrentName, title), + resolution: determineResolution(metaInfo, title), + releaseType: determineReleaseType(metaInfo, title), + } +} + +/** + * Examine the torrent name for language indicators + */ +function determineIfContainsOtherLanguages(torrentName: string, title: string) { + let matches = torrentName.match(/\b(?:Hindi|Telugu|Ita|Italian|Spanish|Latino|Russian|Arabic|Dual|Multi)\b/gi); + for (let match of matches ?? []) { + if (title.indexOf(match) == -1) { + return true; + } + } + return false; +} + +/** + * Interpret the resolution string as an enum value + */ +function resolutionFromString(resolution: string) { + switch(resolution.toUpperCase()) { + case "4K": + case "UHD": + case "2160": + return Resolution.HD4k; + case "1080": + return Resolution.HD1080; + case "720": + return Resolution.HD720; + case "480": + return Resolution.SD480; + case "384": + return Resolution.SD384; + case "360": + return Resolution.SD360; + default: + return Resolution.Unknown; + } +} + +/** + * Determine the video resolution of the torrent + */ +function determineResolution(torrentName: string, title: string) { + let matches = torrentName.match(/\b(?:2160|1080|720|480|384|360)p?|UltraHD|UHD|4K\b/gi); + if (matches == null) { + return Resolution.Unknown; + } + let resolution = matches[matches.length - 1]; + + // Make sure what was matched is not part of the title... + if (matches.length == 1 && title.indexOf(resolution) != -1) { + return Resolution.Unknown; + } + return resolutionFromString(resolution.replace(/p$/i, "")); +} + +/** + * Determine the release type of the torrent + */ +function determineReleaseType(torrentName: string, title: string) { + let releaseTypeRegex: {[type: string]: RegExp} = { + [ReleaseType.BluRay]: /\b(?:BR|Blu-Ray|BluRay|BDRip|BRRip|BDMV|BDR|BD25|BD50|BD5|BD9)\b/i, + [ReleaseType.WebDl] : /\b(?:WEB.?DL|WEB-DLRip)\b/i, + [ReleaseType.WebRip]: /\b(?:WEB.?Rip|WEB)\b/i, + [ReleaseType.WebCap]: /\bWEB.?Cap\b/i, + [ReleaseType.HDRip] : /\b(?:HC|HD.?Rip)\b/i, + [ReleaseType.DVDR] : /\bDVD.?R|DVD-Full|Full-Rip|DVD.?5|DVD.?9\b/i, + [ReleaseType.DVDRip]: /\bDVD.?Rip|DVD.?Mux/i, + [ReleaseType.HDCAM] : /\b(?:TRUE|HD)CAM\b/i, + [ReleaseType.CAM] : /\bCAM.?Rip\b/i, + }; + let matches: RegExpMatchArray | null; + for (let type in releaseTypeRegex) { + matches = torrentName.match(releaseTypeRegex[type]); + if (!matches) { + continue; + } + if (matches.length == 1 || title.indexOf(matches[matches.length - 1]) == -1) { + return parseInt(type); + } + } + return ReleaseType.Unknown; +} diff --git a/src/torrents/providers/Provider.ts b/src/torrents/providers/Provider.ts new file mode 100644 index 0000000..d53c97c --- /dev/null +++ b/src/torrents/providers/Provider.ts @@ -0,0 +1,25 @@ +import { MovieTicket } from "../../database/entities"; +import Torrent from "../Torrent"; + +/** + * Media type flags + */ +export enum MediaType { + None = 0x0, + Movies = 0x1, + TvShows = 0x2 +} + +export default abstract class Provider +{ + /** + * Indicate what media types the provider supports + */ + public static readonly PROVIDES: MediaType = MediaType.None; + + /** + * Search for movies + */ + public abstract searchMovie(movie: MovieTicket): Promise; + +} diff --git a/src/torrents/providers/torrentgalaxy/index.ts b/src/torrents/providers/torrentgalaxy/index.ts new file mode 100644 index 0000000..b628440 --- /dev/null +++ b/src/torrents/providers/torrentgalaxy/index.ts @@ -0,0 +1,30 @@ +import { MovieTicket } from "../../../database/entities"; +import Provider, { MediaType } from "../Provider"; +import Torrent from "../../Torrent"; +import { search, Sort } from "./search"; + + +export default class TorrentGalaxy extends Provider +{ + /** + * Indicate that this provider provides movies + */ + public static readonly PROVIDES = MediaType.Movies; + + /** + * Search for a movie + */ + public async searchMovie(movie: MovieTicket) { + if (movie.imdbId === null) { + return []; + } + let torrents = await search(movie.imdbId, undefined, Sort.Seeders); + return torrents.torrents.map(torrent => new Torrent( + movie, + torrent.name, + torrent.size, + torrent.seeders, + torrent.magnet + )); + } +} diff --git a/src/torrents/providers/torrentgalaxy/search.ts b/src/torrents/providers/torrentgalaxy/search.ts new file mode 100644 index 0000000..5906d83 --- /dev/null +++ b/src/torrents/providers/torrentgalaxy/search.ts @@ -0,0 +1,135 @@ +import cheerio from "cheerio"; +import { request, convertToBytes } from "../../util"; + +const BASE_URL = "https://torrentgalaxy.mx/torrents.php?search="; + +export enum LanguageId { + AllLanguages = 0, + English = 1, + French = 2, + German = 3, + Italian = 4, + Japanese = 5, + Spanish = 6, + Russian = 7, + Hindi = 8, + OtherMultiple = 9, + Korean = 10, + Danish = 11, + Norwegian = 12, + Dutch = 13, + Chinese = 14, + Portuguese = 15, + Bengali = 16, + Polish = 17, + Turkish = 18, + Telugu = 19, + Urdu = 20, + Arabic = 21, + Swedish = 22, + Romanian = 23, + Thai = 24 +} + +export enum Language { + AllLanguages ="AllLanguages", + English ="English", + French ="French", + German ="German", + Italian ="Italian", + Japanese ="Japanese", + Spanish ="Spanish", + Russian ="Russian", + Hindi ="Hindi", + OtherMultiple ="OtherMultiple", + Korean ="Korean", + Danish ="Danish", + Norwegian ="Norwegian", + Dutch ="Dutch", + Chinese ="Chinese", + Portuguese ="Portuguese", + Bengali ="Bengali", + Polish ="Polish", + Turkish ="Turkish", + Telugu ="Telugu", + Urdu ="Urdu", + Arabic ="Arabic", + Swedish ="Swedish", + Romanian ="Romanian", + Thai ="Thai" +} + +export enum Category { + Documentaries = 9, + MoviesHD = 42, + MoviesSD = 1, + Movies4K = 3, + MoviesPacks = 4, + TVEpisodesHD = 41, + TVEPisodesSD = 5, + TVPacks = 6, + TVSports = 7 +} + +export enum Sort { + Date = "id", + Name = "name", + Size = "size", + Seeders = "seeders" +} + +export enum SortOrder { + Asc = "asc", + Desc = "desc", +} + +interface ITorrentGalaxyTorrent { + category: number, + language: Language, + name : string, + magnet : string, + size : number, + seeders : number, + leechers: number +} + +interface ITorrentGalaxyResults { + torrents: ITorrentGalaxyTorrent[], + total_results: number +} + +function scrapeRow($: cheerio.Root, row: cheerio.Cheerio): ITorrentGalaxyTorrent { + let children = row.children(); + let category = $(children[0]).find("a").attr("href")?.split("cat=")[1]; + let language = $(children[2]).find("img[title]").attr("title"); + let name = $(children[3]).text(); + let magnet = $(children[4]).find("a[href^='magnet']").first().attr("href"); + let [size, unit] = $(children[7]).text().split(" "); + let [seeders, leechers] = $(children[10]).text().slice(1, -1).split('/').map(v => parseInt(v)); + return { + category: parseInt(category), + size: convertToBytes(parseFloat(size), unit), + language, name, magnet, seeders, leechers + } +} + +function scrapeResults(response: string): ITorrentGalaxyResults { + let torrents: ITorrentGalaxyTorrent[] = []; + let $ = cheerio.load(response); + $(".tgxtable .tgxtablerow").each((_, elem) => { + torrents.push(scrapeRow($, $(elem))); + }); + return { + torrents, + total_results: parseInt($("#filterbox2 > span").text()) + }; +} + +/** + * Supports IMDb links too + */ +export async function search(query: string, language: LanguageId = LanguageId.AllLanguages, sort: Sort = Sort.Date, order: SortOrder = SortOrder.Desc) { + let res = await request(`${BASE_URL}${encodeURI(query)}&lang=${language}&sort=${sort}&order=${order}`); + let results = scrapeResults(res); + return results; +} diff --git a/src/torrents/ranking.ts b/src/torrents/ranking.ts new file mode 100644 index 0000000..488c305 --- /dev/null +++ b/src/torrents/ranking.ts @@ -0,0 +1,58 @@ +import { Resolution } from "./parsing"; +import Torrent from "./Torrent"; + +/** + * Rank a list of torrents from best to worst to download + */ +export function rankTorrents(torrents: Torrent[]) { + torrents.sort(sortCompare); + let preferred = torrents.filter(selectPreferredTorrents); + return preferred; +} + +/** + * Filter out unwanted torrents + */ +function selectPreferredTorrents(torrent: Torrent) { + if (torrent.seeders == 0 || torrent.metadata.containsOtherLanguage) { + return false; + } + if (torrent.metadata.resolution == Resolution.HD4k) { + return torrent.size != null && torrent.size < 15*1024*1024*1024; // 15GB + } + return true; +} + +/** + * A comparator for ranking torrents + * + * @param a Left side + * @param b Right side + */ +function sortCompare(a: Torrent, b: Torrent) { + // Languages + let languageCmp = a.metadata.containsOtherLanguage - b.metadata.containsOtherLanguage; + if (languageCmp !== 0) { + return languageCmp; + } + // Resolution + let resolutionCmp = a.metadata.resolution - b.metadata.resolution; + if (resolutionCmp !== 0) { + return resolutionCmp; + } + + // If one has only a few seeds, don't worry about the other info. Prioritize seed count + if (a.seeders < 5 || b.seeders < 5) { + let seedersCmp = b.seeders - a.seeders; + if (seedersCmp != 0) { + return seedersCmp; + } + } + + // Sort by the file size + let fileSizeCmp = (a.size ?? 0) - (b.size ?? 0); + if (fileSizeCmp !== 0) { + return fileSizeCmp; + } + return 0; +} diff --git a/src/torrents/util.ts b/src/torrents/util.ts new file mode 100644 index 0000000..cde8844 --- /dev/null +++ b/src/torrents/util.ts @@ -0,0 +1,82 @@ +import { parseString } from "xml2js"; +import https from "https"; + +/** + * Perform an RSS/XML request + */ +export function rssRequest(url: string) { + return new Promise((resolve, reject) => { + https.get(url, { headers: { "User-Agent": "Node", "Accept": "application/rss+xml" } }, (response) => { + if (response.statusCode !== 200) { + reject("Status error: " + response.statusCode); + return; + } + response.setEncoding("utf-8"); + let body = ""; + response.on("data", (chunk) => body += chunk); + response.on("end", () => parseString(body, (err, result) => { + if (err) { + reject(err); + return; + } + resolve(result); + })); + }); + }); +} + +/** + * Perform a generic GET request + */ + export function jsonRequest(url: string) { + return new Promise((resolve, reject) => { + https.get(url, { headers: { "User-Agent": "Node", "Accept": "*/*" } }, (response) => { + if (response.statusCode !== 200) { + reject("Status error: " + response.statusCode); + return; + } + response.setEncoding("utf-8"); + let body = ""; + response.on("data", (chunk) => body += chunk); + response.on("end", () => resolve(JSON.parse(body))); + }); + }); +} + +/** + * Perform a generic GET request + */ + export function request(url: string, timeout: number = 10000) { + return new Promise((resolve, reject) => { + https.get(url, { headers: { "User-Agent": "Node", "Accept": "*/*" }, timeout }, (response) => { + if (response.statusCode !== 200) { + reject("Status error: " + response.statusCode); + return; + } + response.setEncoding("utf-8"); + let body = ""; + response.on("data", (chunk) => body += chunk); + response.on("end", () => resolve(body)); + }).on("timeout", () => reject("timeout")); + }); +} + +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export function convertToBytes(size: number, unit: string, throwUnknownUnit: boolean = true) { + switch(unit.toUpperCase()) { + case "GB": + return Math.ceil(size*1024*1024*1024); + case "MB": + return Math.ceil(size*1024*1024); + case "KB": + return Math.ceil(size*1024*1024); + default: + if (throwUnknownUnit) { + throw new Error("Unknown unit provided"); + } + return Math.ceil(size); + } +} diff --git a/src/typings/node-ipc/index.d.ts b/src/typings/node-ipc/index.d.ts new file mode 100644 index 0000000..276368b --- /dev/null +++ b/src/typings/node-ipc/index.d.ts @@ -0,0 +1,350 @@ +/// + +declare module "node-ipc" { + + import { Socket } from "net"; + + declare const NodeIPC: NodeIPC.NodeIPC; + + declare namespace NodeIPC { + interface NodeIPC extends IPC + {} + + interface IPC { + /** + * Set these variables in the ipc.config scope to overwrite or set default values + */ + config: Config; + /** + * https://www.npmjs.com/package/node-ipc#log + */ + log(...args: any[]): void; + /** + * https://www.npmjs.com/package/node-ipc#connectto + * Used for connecting as a client to local Unix Sockets and Windows Sockets. + * This is the fastest way for processes on the same machine to communicate + * because it bypasses the network card which TCP and UDP must both use. + * @param id is the string id of the socket being connected to. + * The socket with this id is added to the ipc.of object when created. + * @param path is the path of the Unix Domain Socket File, if the System is Windows, + * this will automatically be converted to an appropriate pipe with the same information as the Unix Domain Socket File. + * If not set this will default to ipc.config.socketRoot+ipc.config.appspace+id + * @param callback this is the function to execute when the socket has been created + */ + connectTo(id: string, path?: string, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#connectto + * Used for connecting as a client to local Unix Sockets and Windows Sockets. + * This is the fastest way for processes on the same machine to communicate + * because it bypasses the network card which TCP and UDP must both use. + * @param id is the string id of the socket being connected to. + * The socket with this id is added to the ipc.of object when created. + * @param callback this is the function to execute when the socket has been created + */ + connectTo(id: string, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#connecttonet + * Used to connect as a client to a TCP or TLS socket via the network card. + * This can be local or remote, if local, it is recommended that you use the Unix + * and Windows Socket Implementaion of connectTo instead as it is much faster since it avoids the network card altogether. + * For TLS and SSL Sockets see the node-ipc TLS and SSL docs. + * They have a few additional requirements, and things to know about and so have their own doc. + * @param id is the string id of the socket being connected to. For TCP & TLS sockets, + * this id is added to the ipc.of object when the socket is created with a reference to the socket + * @param host is the host on which the TCP or TLS socket resides. + * This will default to ipc.config.networkHost if not specified + * @param port the port on which the TCP or TLS socket resides + * @param callback this is the function to execute when the socket has been created + */ + connectToNet(id: string, host?: string, port?: number, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#connecttonet + * Used to connect as a client to a TCP or TLS socket via the network card. + * This can be local or remote, if local, it is recommended that you use the Unix + * and Windows Socket Implementaion of connectTo instead as it is much faster since it avoids the network card altogether. + * For TLS and SSL Sockets see the node-ipc TLS and SSL docs. + * They have a few additional requirements, and things to know about and so have their own doc. + * @param id is the string id of the socket being connected to. For TCP & TLS sockets, + * this id is added to the ipc.of object when the socket is created with a reference to the socket + * @param callback this is the function to execute when the socket has been created + */ + connectToNet(id: string, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#connecttonet + * Used to connect as a client to a TCP or TLS socket via the network card. + * This can be local or remote, if local, it is recommended that you use the Unix + * and Windows Socket Implementaion of connectTo instead as it is much faster since it avoids the network card altogether. + * For TLS and SSL Sockets see the node-ipc TLS and SSL docs. + * They have a few additional requirements, and things to know about and so have their own doc. + * @param id is the string id of the socket being connected to. + * For TCP & TLS sockets, this id is added to the ipc.of object when the socket is created with a reference to the socket + * @param host is the host on which the TCP or TLS socket resides. This will default to ipc.config.networkHost if not specified + * @param port the port on which the TCP or TLS socket resides + * @param callback this is the function to execute when the socket has been created + */ + connectToNet(id: string, hostOrPort: number | string, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#disconnect + * Used to disconnect a client from a Unix, Windows, TCP or TLS socket. + * The socket and its refrence will be removed from memory and the ipc.of scope. + * This can be local or remote. UDP clients do not maintain connections and so there are no Clients and this method has no value to them + * @param id is the string id of the socket from which to disconnect + */ + disconnect(id: string): void; + /** + * https://www.npmjs.com/package/node-ipc#serve + * Used to create local Unix Socket Server or Windows Socket Server to which Clients can bind. + * The server can emit events to specific Client Sockets, or broadcast events to all known Client Sockets + * @param path This is the path of the Unix Domain Socket File, if the System is Windows, + * this will automatically be converted to an appropriate pipe with the same information as the Unix Domain Socket File. + * If not set this will default to ipc.config.socketRoot+ipc.config.appspace+id + * @param callback This is a function to be called after the Server has started. + * This can also be done by binding an event to the start event like ipc.server.on('start',function(){}); + */ + serve(path: string, callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#serve + * Used to create local Unix Socket Server or Windows Socket Server to which Clients can bind. + * The server can emit events to specific Client Sockets, or broadcast events to all known Client Sockets + * @param callback This is a function to be called after the Server has started. + * This can also be done by binding an event to the start event like ipc.server.on('start',function(){}); + */ + serve(callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#serve + * Used to create local Unix Socket Server or Windows Socket Server to which Clients can bind. + * The server can emit events to specific Client Sockets, or broadcast events to all known Client Sockets + */ + serve(callback: null): void; + /** + * https://www.npmjs.com/package/node-ipc#servenet + * @param host If not specified this defaults to the first address in os.networkInterfaces(). + * For TCP, TLS & UDP servers this is most likely going to be 127.0.0.1 or ::1 + * @param port The port on which the TCP, UDP, or TLS Socket server will be bound, this defaults to 8000 if not specified + * @param UDPType If set this will create the server as a UDP socket. 'udp4' or 'udp6' are valid values. + * This defaults to not being set. When using udp6 make sure to specify a valid IPv6 host, like ::1 + * @param callback Function to be called when the server is created + */ + serveNet(host?: string, port?: number, UDPType?: "udp4" | "udp6", callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#servenet + * @param UDPType If set this will create the server as a UDP socket. 'udp4' or 'udp6' are valid values. + * This defaults to not being set. When using udp6 make sure to specify a valid IPv6 host, like ::1 + * @param callback Function to be called when the server is created + */ + serveNet(UDPType: "udp4" | "udp6", callback?: () => void): void; + /** + * https://www.npmjs.com/package/node-ipc#servenet + * @param callback Function to be called when the server is created + * @param port The port on which the TCP, UDP, or TLS Socket server will be bound, this defaults to 8000 if not specified + */ + serveNet(callbackOrPort: EmptyCallback | number): void; + /** + * https://www.npmjs.com/package/node-ipc#servenet + * @param host If not specified this defaults to the first address in os.networkInterfaces(). + * For TCP, TLS & UDP servers this is most likely going to be 127.0.0.1 or ::1 + * @param port The port on which the TCP, UDP, or TLS Socket server will be bound, this defaults to 8000 if not specified + * @param callback Function to be called when the server is created + */ + serveNet(host: string, port: number, callback?: () => void): void; + /** + * This is where socket connection refrences will be stored when connecting to them as a client via the ipc.connectTo + * or iupc.connectToNet. They will be stored based on the ID used to create them, eg : ipc.of.mySocket + */ + of: any; + /** + * This is a refrence to the server created by ipc.serve or ipc.serveNet + */ + server: Server; + } + type EmptyCallback = () => void; + interface Client { + /** + * triggered when a JSON message is received. The event name will be the type string from your message + * and the param will be the data object from your message eg : { type:'myEvent',data:{a:1}} + */ + on(event: string, callback: (message: any, socket: Socket) => void): Client; + /** + * triggered when an error has occured + */ + on(event: "error", callback: (err: any) => void): Client; + /** + * connect - triggered when socket connected + * disconnect - triggered by client when socket has disconnected from server + * destroy - triggered when socket has been totally destroyed, no further auto retries will happen and all references are gone + */ + on(event: "connect" | "disconnect" | "destroy", callback: () => void): Client; + /** + * triggered by server when a client socket has disconnected + */ + on(event: "socket.disconnected", callback: (socket: Socket, destroyedSocketID: string) => void): Client; + /** + * triggered when ipc.config.rawBuffer is true and a message is received + */ + on(event: "data", callback: (buffer: Buffer) => void): Client; + emit(event: string, value?: any): Client; + /** + * Unbind subscribed events + */ + off(event: string, handler: any): Client; + } + interface Server extends Client { + /** + * start serving need top call serve or serveNet first to set up the server + */ + start(): void; + /** + * close the server and stop serving + */ + stop(): void; + emit(value: any): Client; + emit(event: string, value: any): Client; + emit(socket: Socket | SocketConfig, event: string, value?: any): Server; + emit(socketConfig: Socket | SocketConfig, value?: any): Server; + } + interface SocketConfig { + address?: string; + port?: number; + } + interface Config { + /** + * Default: 'app.' + * Used for Unix Socket (Unix Domain Socket) namespacing. + * If not set specifically, the Unix Domain Socket will combine the socketRoot, appspace, + * and id to form the Unix Socket Path for creation or binding. + * This is available incase you have many apps running on your system, you may have several sockets with the same id, + * but if you change the appspace, you will still have app specic unique sockets + */ + appspace: string; + /** + * Default: '/tmp/' + * The directory in which to create or bind to a Unix Socket + */ + socketRoot: string; + /** + * Default: os.hostname() + * The id of this socket or service + */ + id: string; + /** + * Default: 'localhost' + * The local or remote host on which TCP, TLS or UDP Sockets should connect + * Should resolve to 127.0.0.1 or ::1 see the table below related to this + */ + networkHost: string; + /** + * Default: 8000 + * The default port on which TCP, TLS, or UDP sockets should connect + */ + networkPort: number; + /** + * Default: 'utf8' + * the default encoding for data sent on sockets. Mostly used if rawBuffer is set to true. + * Valid values are : ascii utf8 utf16le ucs2 base64 hex + */ + encoding: "ascii" | "utf8" | "utf16le" | "ucs2" | "base64" | "hex"; + /** + * Default: false + * If true, data will be sent and received as a raw node Buffer NOT an Object as JSON. + * This is great for Binary or hex IPC, and communicating with other processes in languages like C and C++ + */ + rawBuffer: boolean; + /** + * Default: false + * Synchronous requests. Clients will not send new requests until the server answers + */ + sync: boolean; + /** + * Default: false + * Turn on/off logging default is false which means logging is on + */ + silent: boolean; + /** + * Default: true + * Turn on/off util.inspect colors for ipc.log + */ + logInColor: boolean; + /** + * Default: 5 + * Set the depth for util.inspect during ipc.log + */ + logDepth: number; + /** + * Default: console.log + * The function which receives the output from ipc.log; should take a single string argument + */ + logger(msg: string): void; + /** + * Default: 100 + * This is the max number of connections allowed to a socket. It is currently only being set on Unix Sockets. + * Other Socket types are using the system defaults + */ + maxConnections: number; + /** + * Default: 500 + * This is the time in milliseconds a client will wait before trying to reconnect to a server if the connection is lost. + * This does not effect UDP sockets since they do not have a client server relationship like Unix Sockets and TCP Sockets + */ + retry: number; + /* */ + /** + * Default: false + * if set, it represents the maximum number of retries after each disconnect before giving up + * and completely killing a specific connection + */ + maxRetries: boolean | number; + /** + * Default: false + * Defaults to false meaning clients will continue to retry to connect to servers indefinitely at the retry interval. + * If set to any number the client will stop retrying when that number is exceeded after each disconnect. + * If set to true in real time it will immediately stop trying to connect regardless of maxRetries. + * If set to 0, the client will NOT try to reconnect + */ + stopRetrying: boolean; + /** + * Default: true + * Defaults to true meaning that the module will take care of deleting the IPC socket prior to startup. + * If you use node-ipc in a clustered environment where there will be multiple listeners on the same socket, + * you must set this to false and then take care of deleting the socket in your own code. + */ + unlink: boolean; + /** + * Primarily used when specifying which interface a client should connect through. + * see the socket.connect documentation in the node.js api https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener + */ + interfaces: { + /** + * Default: false + */ + localAddress?: boolean; + /** + * Default: false + */ + localPort?: boolean; + /** + * Default: false + */ + family?: boolean; + /** + * Default: false + */ + hints?: boolean; + /** + * Default: false + */ + lookup?: boolean; + }; + tls: { + rejectUnauthorized?: boolean; + public?: string; + private?: string; + }; + } + } + + export = NodeIPC + // declare const RootIPC: NodeIPC.IPC & { IPC: new () => NodeIPC.IPC }; + + // export = RootIPC; +} + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fc943ed --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,71 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + "typeRoots": ["./src/typings"], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + "sourceRoot": "./src", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/yarn.lock b/yarn.lock index 88a53b4..aff1839 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,62 @@ # yarn lockfile v1 +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "@sqltools/formatter@^1.2.2": version "1.2.3" resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20" integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg== +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@types/node@*", "@types/node@^14.14.41": + version "14.14.41" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" + integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== + +"@types/xml2js@^0.4.8": + version "0.4.8" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.8.tgz#84c120c864a5976d0b5cf2f930a75d850fc2b03a" + integrity sha512-EyvT83ezOdec7BhDaEcsklWy7RSIdi6CNe95tmOAK0yx/Lm30C9K75snT3fYayK59ApC2oyW+rcHErdG05FHJA== + dependencies: + "@types/node" "*" + "@types/zen-observable@^0.8.2": version "0.8.2" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -39,6 +80,14 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= +anymatch@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + app-root-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" @@ -64,6 +113,35 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bignumber.js@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" + integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -72,6 +150,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -85,6 +170,24 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + chalk@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -96,6 +199,14 @@ chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -104,6 +215,54 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +cheerio-select@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9" + integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew== + dependencies: + css-select "^4.1.2" + css-what "^5.0.0" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.6.0" + +cheerio@^1.0.0-rc.6: + version "1.0.0-rc.6" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.6.tgz#a5ae81ab483aeefa1280c325543c601145506240" + integrity sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw== + dependencies: + cheerio-select "^1.3.0" + dom-serializer "^1.3.1" + domhandler "^4.1.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + +chokidar@^3.2.2: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-highlight@^2.1.10: version "2.1.11" resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" @@ -125,6 +284,13 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -142,11 +308,63 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-select@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286" + integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47" + integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA== + +debug@^2.2.0: + 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.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" @@ -154,47 +372,171 @@ debug@^4.3.1: dependencies: ms "2.1.2" +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dom-serializer@^1.0.1, dom-serializer@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be" + integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.1.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7" + integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + dotenv@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +easy-stack@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.1.tgz#8afe4264626988cabb11f3c704ccd0c835411066" + integrity sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +event-pubsub@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" + integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== + figlet@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.0.tgz#2db4d00a584e5155a96080632db919213c3e003c" integrity sha512-ZQJM4aifMpz6H19AW1VqvZ7l4pOE9p7i/3LyxgO2kp+PO/VcDYNqIHEMtkccqIhTXMKci4kjueJr/iCQEaT/Ww== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -glob@^7.1.6: +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.3, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -206,6 +548,35 @@ glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" + integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== + dependencies: + ini "1.3.7" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.2: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -213,21 +584,61 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + highlight.js@^10.7.1: version "10.7.2" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -236,16 +647,112 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1: +inherits@2, inherits@^2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +js-message@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47" + integrity sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA== + +js-queue@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/js-queue/-/js-queue-2.0.2.tgz#0be590338f903b36c73d33c31883a821412cd482" + integrity sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA== + dependencies: + easy-stack "^1.0.1" + js-yaml@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -253,11 +760,52 @@ js-yaml@^4.0.0: dependencies: argparse "^2.0.1" +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -265,16 +813,41 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimist@^1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mysql@^2.18.1: + version "2.18.1" + resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" + integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig== + dependencies: + bignumber.js "9.0.0" + readable-stream "2.3.7" + safe-buffer "5.1.2" + sqlstring "2.3.1" + mz@^2.4.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -284,24 +857,88 @@ mz@^2.4.0: object-assign "^4.0.1" thenify-all "^1.0.0" +node-ipc@^9.1.4: + version "9.1.4" + resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.1.4.tgz#2acf962681afdac2602876d98fe6434d54d9bd3c" + integrity sha512-A+f0mn2KxUt1uRTSd5ktxQUsn2OEhj5evo7NUi/powBzMSZ0vocdzDjlq9QN2v3LH6CJi3e5xAenpZ1QwU5A8g== + dependencies: + event-pubsub "4.3.0" + js-message "1.0.7" + js-queue "2.0.2" + +nodemon@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.7.tgz#6f030a0a0ebe3ea1ba2a38f71bf9bab4841ced32" + integrity sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA== + dependencies: + chokidar "^3.2.2" + debug "^3.2.6" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.7" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.3" + update-notifier "^4.1.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + parent-require@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977" integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc= -parse5-htmlparser2-tree-adapter@^6.0.0: +parse5-htmlparser2-tree-adapter@^6.0.0, parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== @@ -323,16 +960,114 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" + integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +pstree.remy@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pupa@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -343,6 +1078,23 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + sha.js@^2.4.11: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -351,6 +1103,11 @@ sha.js@^2.4.11: inherits "^2.0.1" safe-buffer "^5.0.1" +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" @@ -364,7 +1121,21 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -string-width@^4.1.0, string-width@^4.2.0: +sqlstring@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" + integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== @@ -373,6 +1144,13 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -380,6 +1158,13 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -387,11 +1172,23 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -399,6 +1196,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +term-size@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -413,6 +1215,25 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + ts-node@^9.1.1: version "9.1.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" @@ -430,6 +1251,18 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typeorm@^0.2.32: version "0.2.32" resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.32.tgz#544dbfdfe0cd0887548d9bcbd28527ea4f4b3c9b" @@ -458,6 +1291,58 @@ typescript@^4.2.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +undefsafe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" + integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== + dependencies: + debug "^2.2.0" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +update-notifier@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" + integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -472,6 +1357,21 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + xml2js@^0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"