diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9529102
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/node_modules
+/dist
+/temp
+/ffmpeg/*
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..61ab961
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..99aa364
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+[![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-blue)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
+
+
Hidive Downloader
+
+# Features
+- User friendly (Completely CLI controlled)
+- Download episodes simultaneously
+- Search function
+- Video files are converted to MKV lossless
+- Subtitles are added to MKV
+
+# How to use
+- Download the latest [release](https://github.com/philhk/hidive-downloader/releases)
+- Extract the content to a folder
+- Run `npm install` > `npm run build` in a terminal
+- After that you can run the application with `npm run start` in a terminal
+
+# Example Video
+https://user-images.githubusercontent.com/66958695/194079411-073d6dfe-6886-4d64-83bb-2679fe3a4f6a.mp4
\ No newline at end of file
diff --git a/db/database.db b/db/database.db
new file mode 100644
index 0000000..918049e
Binary files /dev/null and b/db/database.db differ
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..37e0d90
--- /dev/null
+++ b/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "hidive-downloader",
+ "main": "index.js",
+ "scripts": {
+ "start:dev": "ts-node-esm src/main.ts",
+ "start": "node dist/main.js",
+ "build:tsc": "tsc",
+ "build": "npm run build:tsc"
+ },
+ "type": "module",
+ "author": "PhilHK",
+ "devDependencies": {
+ "@types/async": "^3.2.15",
+ "@types/better-sqlite3": "^7.6.0",
+ "@types/cli-progress": "^3.11.0",
+ "@types/ffmpeg": "^1.0.4",
+ "@types/fs-extra": "^9.0.13",
+ "@types/inquirer": "^9.0.2",
+ "@types/node": "^18.8.0",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.8.4"
+ },
+ "dependencies": {
+ "async": "^3.2.4",
+ "axios": "^0.27.2",
+ "better-sqlite3": "^7.6.2",
+ "cli-progress": "^3.11.2",
+ "fs-extra": "^10.1.0",
+ "inquirer": "^9.1.2",
+ "m3u8-parser": "^6.0.0"
+ }
+}
diff --git a/sql/createDatabase.sql b/sql/createDatabase.sql
new file mode 100644
index 0000000..7e0edac
--- /dev/null
+++ b/sql/createDatabase.sql
@@ -0,0 +1,17 @@
+-- SQLite
+DROP TABLE IF EXISTS Account;
+DROP TABLE IF EXISTS ApiKey;
+CREATE TABLE Account (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ email VARCHAR(50) NOT NULL,
+ password VARCHAR(50) NOT NULL,
+ userId INTEGER,
+ profileId INTEGER,
+ deviceId VARCHAR(50),
+ visitId VARCHAR(50)
+);
+CREATE TABLE ApiKey (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ key VARCHAR(50) NOT NULL
+);
+INSERT INTO ApiKey (key) VALUES ('508efd7b42d546e19cc24f4d0b414e57e351ca73');
\ No newline at end of file
diff --git a/src/api/HidiveDao.ts b/src/api/HidiveDao.ts
new file mode 100644
index 0000000..72b2229
--- /dev/null
+++ b/src/api/HidiveDao.ts
@@ -0,0 +1,579 @@
+import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosInterceptorManager } from "axios"
+import crypto from 'crypto'
+import { ApiKey } from "../models/ApiKey"
+import { User } from "../models/User"
+//@ts-expect-error
+import { Parser } from 'm3u8-parser'
+
+export interface Manifest {
+ allowCache: boolean,
+ endList: boolean,
+ mediaSequence: number,
+ discontinuitySequence: number,
+ playlistType: string,
+ custom: {},
+ playlists: [
+ {
+ attributes: T,
+ uri: string
+ }
+ ],
+ mediaGroups: {
+ AUDIO: {
+ 'GROUP-ID': {
+ NAME: {
+ default: boolean,
+ autoselect: boolean,
+ language: string,
+ uri: string,
+ instreamId: string,
+ characteristics: string,
+ forced: boolean
+ }
+ }
+ },
+ VIDEO: {},
+ 'CLOSED-CAPTIONS': {},
+ SUBTITLES: {}
+ },
+ dateTimeString: string,
+ dateTimeObject: Date,
+ targetDuration: number,
+ totalDuration: number,
+ discontinuityStarts: [number],
+ segments: [
+ {
+ byterange: {
+ length: number,
+ offset: number
+ },
+ duration: number,
+ attributes: {},
+ discontinuity: number,
+ uri: string,
+ timeline: number,
+ key: {
+ method: string,
+ uri: string,
+ iv: string
+ },
+ map: {
+ uri: string,
+ byterange: {
+ length: number,
+ offset: number
+ }
+ },
+ 'cue-out': string,
+ 'cue-out-cont': string,
+ 'cue-in': string,
+ custom: {}
+ } & D
+ ]
+}
+
+export const BASEROUTE = 'https://api.hidive.com/api/v1'
+export const ROUTES = {
+ ping: () => `${BASEROUTE}/Ping`,
+ initDevice: () => `${BASEROUTE}/InitDevice`,
+ initVisit: () => `${BASEROUTE}/InitVisit`,
+ getGlobalSettings: () => `${BASEROUTE}/GetGlobalSettings`,
+ authenticate: () => `${BASEROUTE}/Authenticate`,
+ getDashboard: () => `${BASEROUTE}/GetDashboard`,
+ getTitle: () => `${BASEROUTE}/GetTitle`,
+ getMightLike: () => `${BASEROUTE}/GetMightLike`,
+ getVideos: () => `${BASEROUTE}/GetVideos`,
+ startVideo: () => `${BASEROUTE}/StartVideo`,
+ search: () => `${BASEROUTE}/Search`,
+}
+
+export interface RequiredHidiveDaoOptions {
+ user: User,
+ apiKey: ApiKey
+}
+export interface OptionalHidiveDaoOptions {
+
+}
+export type HidiveDaoOptions = RequiredHidiveDaoOptions & OptionalHidiveDaoOptions
+export type UserHidiveDaoOptions = RequiredHidiveDaoOptions & Partial
+
+export interface RESTApiStandardResponseBody {
+ Code: number,
+ Status: string,
+ Message: string | null,
+ Messages: any,
+ Data: T,
+ Timestamp: string,
+ IPAddress: string
+}
+
+export interface RESTApiPostPingResponseBodyData {
+
+}
+
+export interface RESTApiPostPingResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface RESTApiPostInitDeviceRequestBody {
+ DeviceName: string,
+ DeviceProfile?: {
+ DeviceType: number,
+ HardwareModel: number,
+ HardwareName: number,
+ HardwareVendor: number,
+ PlatformVendor: number,
+ PlatformVersion: number
+ },
+ MacAddress?: string
+}
+
+export interface RESTApiPostInitDeviceResponseBodyData {
+ DeviceId: string,
+ VisitId: string
+}
+
+export interface RESTApiPostInitDeviceResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface RESTApiUser {
+ Id: number,
+ Email: null | string,
+ CountryCode: null | string,
+ CountryName: null | string,
+ ServiceLevel: string,
+ NextBillDate: null | string
+}
+
+export interface RESTApiProfile {
+ Id: number,
+ Primary: boolean,
+ Nickname: string,
+ PinEnabled: boolean,
+ AvatarJPGUrl: string,
+ AvatarPNGUrl: string,
+ AllowMatureContent: boolean,
+ PreferSubsOverDubs: boolean,
+ PreferredLanguage: string,
+ PreferredSubtitleColor: number
+}
+
+export interface RESTApiPostInitVisitResponseBodyData {
+ VisitId: string,
+ User: RESTApiUser,
+ Profiles: RESTApiProfile[]
+}
+
+export interface RESTApiPostInitVisitResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface RESTApiPostAuthenticateRequestBody {
+ Email: string,
+ Password: string
+}
+
+export interface RESTApiPostAuthenticateResponseBodyData {
+ VisitId: string,
+ User: RESTApiUser,
+ Profiles: RESTApiProfile[]
+}
+
+export interface RESTApiPostAuthenticateResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface RESTApiPostGetTitleRequestBody {
+ Id: number
+}
+
+export interface RESTApiEpisode {
+ Id: number,
+ Number: number,
+ Name: string,
+ Summary: string,
+ HIDIVEPremiereDate: string,
+ ScreenShotSmallUrl: string,
+ ScreenShotCompressedUrl: string,
+ SeasonNumber: number,
+ TitleId: number,
+ SeasonNumberValue: number,
+ EpisodeNumberValue: number,
+ VideoKey: string,
+ DisplayNameLong: string,
+ PercentProgress: number,
+ LoadTime: number
+}
+
+export interface RESTApiTitle {
+ Id: number,
+ Name: string,
+ ShortSynopsis: string,
+ MediumSynopsis: string,
+ LongSynopsis: string,
+ KeyArtUrl: string,
+ MasterArtUrl: string,
+ Rating: string,
+ OverallRating: number,
+ RatingCount: number,
+ MALScore: string,
+ UserRating: number,
+ RunTime: number,
+ ShowInfoTitle: string,
+ FirstPremiereDate: string,
+ EpisodeCount: number,
+ SeasonName: string,
+ RokuHDArtUrl: string,
+ RokuSDArtUrl: string,
+ IsRateable: boolean,
+ InQueue: boolean,
+ IsFavorite: boolean,
+ IsContinueWatching: boolean,
+ ContinueWatching: null | boolean,
+ Episodes: RESTApiEpisode[],
+ LoadTime: number
+}
+
+export interface RESTApiPostGetTitleResponseBodyData {
+ Title: RESTApiTitle
+}
+
+export interface RESTApiPostGetTitleResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+
+
+export const defaultHidiveDaoOptions: OptionalHidiveDaoOptions & Partial = {
+
+}
+
+export interface RESTApiPostGetVideosRequestBody {
+ TitleId: number,
+ VideoKey: string
+}
+
+export interface RESTApiPostGetVideosResponseBodyData {
+ ShowAds: boolean,
+ CaptionCssUrl: string,
+ FontSize: number,
+ FontScale: number,
+ CaptionLanguages: string[],
+ CaptionLanguage: string,
+ CaptionVttUrls: {
+ [key: string]: string
+ }
+ VideoLanguages: string[],
+ VideoLanguage: string,
+ VideoUrls: {
+ [key: string]: {
+ hls: string[],
+ drm: string[],
+ drmEnabled: boolean
+ }
+ },
+ FontColorName: string,
+ AutoPlayNextEpisode: boolean,
+ MaxStreams: number,
+ CurrentTime: number,
+ FontColorCode: string,
+ RunTime: number,
+ AdUrl: null | string
+}
+
+export interface RESTApiPostGetVideosResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface RESTApiPostSearchRequestBody {
+ Query: string
+}
+
+export interface RESTApiPostSearchResponseBodyData {
+ Query: string,
+ Slug: string,
+ TitleResults: RESTApiTitle[],
+ SearchId: number,
+ IsSearchedPinned: boolean,
+ IsPinnedSearchAvailable: boolean
+}
+
+export interface RESTApiPostSearchResponseBody extends RESTApiStandardResponseBody {
+
+}
+
+export interface Resolution {
+ width: number,
+ height: number
+}
+
+export interface RESTApiMediaSource {
+ 'program-Id': number,
+ BANDWIDTH: number,
+ RESOLUTION: Resolution,
+ CODECS: string[],
+}
+
+export interface RESTApiMedia {
+ duration: number,
+ uri: string,
+ key: {
+ method: string,
+ uri: string,
+ iv: Uint32Array
+ },
+ timeline: number
+}
+
+export type RESTApiResponse =
+ Partial>
+ & Omit
+ & {
+ Data: Partial<
+ & RESTApiPostGetVideosResponseBodyData
+ & RESTApiPostAuthenticateResponseBodyData
+ & RESTApiPostPingResponseBodyData
+ & RESTApiPostGetTitleResponseBodyData
+ & RESTApiPostInitDeviceResponseBodyData
+ & RESTApiPostInitVisitResponseBodyData>
+ }
+
+export interface XHeadersHidive {
+ 'X-Applicationid': string,
+ 'X-Deviceid': string,
+ 'X-Nonce': string,
+ 'X-Profileid': number,
+ 'X-Signature': string,
+ 'X-Userid': number,
+ 'X-Visitid': string
+}
+
+export const defaultXHeadersHidive: XHeadersHidive = {
+ "X-Applicationid": '24i-Android',
+ "X-Profileid": 0,
+ "X-Userid": 0,
+ "X-Deviceid": '',
+ "X-Nonce": '',
+ "X-Signature": '',
+ "X-Visitid": ''
+}
+
+export class HidiveDao {
+ private readonly axios: AxiosInstance
+ public readonly options: HidiveDaoOptions
+ private readonly axiosOptions: AxiosRequestConfig
+ private xHeadersHidive: XHeadersHidive
+ public user?: RESTApiUser
+ public profile?: RESTApiProfile
+ private ipAddress?: string
+ private cookie: { [key: string]: string } = {}
+
+ constructor(options: UserHidiveDaoOptions) {
+ this.options = {
+ ...defaultHidiveDaoOptions,
+ ...options
+ }
+ this.xHeadersHidive = {
+ ...defaultXHeadersHidive,
+ ...(this.options.user.userId ? { "X-Userid": this.options.user.userId } : {}),
+ ...(this.options.user.profileId ? { "X-Profileid": this.options.user.profileId } : {}),
+ ...(this.options.user.deviceId ? { "X-Deviceid": this.options.user.deviceId } : {}),
+ ...(this.options.user.visitId ? { "X-Visitid": this.options.user.visitId } : {})
+ }
+ this.axiosOptions = {
+ headers: {
+ 'accept-encoding': 'gzip, deflate, br',
+ 'user-agent': 'okhttp/3.12.1',
+ 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
+ },
+ }
+ this.axios = axios.create({
+ ...this.axiosOptions
+ })
+ this.setInterceptors()
+ }
+
+ private setInterceptors() {
+ this.axios.interceptors.request.use((config) => {
+ const { data } = config
+
+ this.xHeadersHidive["X-Nonce"] = this.generateNonce()
+ this.xHeadersHidive["X-Signature"] = this.generateSignature(data)
+ config.headers = {
+ ...config.headers,
+ 'X-Applicationid': this.xHeadersHidive["X-Applicationid"],
+ 'X-Deviceid': this.xHeadersHidive["X-Deviceid"],
+ 'X-Nonce': this.xHeadersHidive["X-Nonce"],
+ 'X-Profileid': this.xHeadersHidive["X-Profileid"],
+ 'X-Signature': this.xHeadersHidive["X-Signature"],
+ 'X-Userid': this.xHeadersHidive["X-Userid"],
+ 'X-Visitid': this.xHeadersHidive["X-Visitid"],
+ ...(Object.entries(this.cookie).length > 0 ? { cookie: Object.entries(this.cookie).map(cookie => `${cookie[0]}=${cookie[1]}`).join('; ') } : {})
+ }
+
+ return config
+ });
+
+
+ (this.axios.interceptors.response as AxiosInterceptorManager>).use((res) => {
+ const { data, headers, config } = res
+ if (headers["set-cookie"]) {
+ this.cookie = {
+ ...this.cookie,
+ ...Object.assign({}, ...headers["set-cookie"].map(cookie => {
+ const [name, value] = cookie.split(';')[0].split('=')
+ return {
+ [name]: value
+ }
+ }))
+ }
+ }
+
+ if (config.url?.startsWith(BASEROUTE) && data.Data) {
+ const { IPAddress } = data
+
+ if (data.Data.User)
+ this.user = data.Data.User
+
+ if (data.Data.Profiles && data.Data.Profiles.length > 0)
+ this.profile = data.Data.Profiles.find(profile => profile.Primary) || data.Data.Profiles[0]
+
+ if (IPAddress)
+ this.ipAddress = IPAddress
+
+ this.xHeadersHidive = {
+ ...this.xHeadersHidive,
+ ...(data.Data.DeviceId ? { "X-Deviceid": data.Data.DeviceId } : {}),
+ ...(data.Data.VisitId ? { "X-Visitid": data.Data.VisitId } : {}),
+ ...(this.user ? { "X-Userid": this.user.Id } : {}),
+ ...(this.profile ? { "X-Profileid": this.profile.Id } : {}),
+ }
+
+ if (data.Code !== 0 || data.Status !== 'Success') {
+ return Promise.reject(new Error(`Response returned code ${data.Code} with status ${data.Status}`))
+ }
+ }
+
+ return res
+ })
+ }
+
+ private generateNonce() {
+ const initDate = new Date();
+ const nonceDate = [
+ initDate.getUTCFullYear().toString().slice(-2),
+ ('0' + (initDate.getUTCMonth() + 1)).slice(-2),
+ ('0' + initDate.getUTCDate()).slice(-2),
+ ('0' + initDate.getUTCHours()).slice(-2),
+ ('0' + initDate.getUTCMinutes()).slice(-2)
+ ].join('');
+ const nonceCleanStr = nonceDate + this.options.apiKey.key;
+ const nonceHash = crypto.createHash('sha256').update(nonceCleanStr).digest('hex');
+ return nonceHash;
+ }
+
+ private generateSignature(requestBody: any) {
+ const sigCleanStr = [
+ this.ipAddress,
+ this.xHeadersHidive["X-Applicationid"],
+ this.xHeadersHidive["X-Deviceid"],
+ this.xHeadersHidive["X-Visitid"],
+ this.xHeadersHidive["X-Userid"],
+ this.xHeadersHidive["X-Profileid"],
+ JSON.stringify(requestBody),
+ this.xHeadersHidive["X-Nonce"],
+ this.options.apiKey.key,
+ ].join('');
+ return crypto.createHash('sha256').update(sigCleanStr).digest('hex');
+ }
+
+ public async ping() {
+ return (await this.axios.post(ROUTES.ping())).data
+ }
+
+ public async initDevice() {
+ return (await this.axios.post, RESTApiPostInitDeviceRequestBody>(ROUTES.initDevice(), {
+ DeviceName: 'Android'
+ })).data
+ }
+
+ public async initVisit() {
+ return (await this.axios.post(ROUTES.initVisit(), {
+
+ })).data
+ }
+
+ public async authenticate() {
+ return (await this.axios.post, RESTApiPostAuthenticateRequestBody>(ROUTES.authenticate(), {
+ Email: this.options.user.email,
+ Password: this.options.user.password
+ })).data
+ }
+
+ public async getTitle(id: number) {
+ return (await this.axios.post, RESTApiPostGetTitleRequestBody>(ROUTES.getTitle(), {
+ Id: id
+ })).data
+ }
+
+ public async getVideos(titleId: number, VideoKey: string) {
+ return (await this.axios.post, RESTApiPostGetVideosRequestBody>(ROUTES.getVideos(), {
+ TitleId: titleId,
+ VideoKey: VideoKey
+ })).data
+ }
+
+ public async search(query: string) {
+ return (await this.axios.post, RESTApiPostSearchRequestBody>(ROUTES.search(), {
+ Query: query
+ })).data
+ }
+
+ public async getMedia(mediaUrl: string): Promise> {
+ const parser = new Parser()
+ parser.push((await this.axios.get(mediaUrl)).data)
+ parser.end()
+ return parser.manifest
+ }
+
+ public async getMediaSources(mediaSourceUrl: string): Promise> {
+ const parser = new Parser()
+ parser.push((await this.axios.get(mediaSourceUrl)).data)
+ parser.end()
+ return parser.manifest
+ }
+
+ public async getTs(tsUrl: string): Promise {
+ return (await this.axios.get(tsUrl, {
+ headers: {
+ 'accept-encoding': 'identity',
+ 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2'
+ },
+ responseType: 'arraybuffer'
+ })).data
+ }
+
+ public async getKey(keyUrl: string): Promise {
+ return (await this.axios.get(keyUrl, {
+ headers: {
+ 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2'
+ },
+ responseType: 'arraybuffer'
+ })).data
+ }
+
+ public async getSub(subUrl: string): Promise {
+ return (await this.axios.get(subUrl, {
+ headers: {
+ 'user-agent': 'smartexoplayer/1.9.20.R (Linux;Android 9) ExoPlayerLib/2.7.2'
+ }
+ })).data
+ }
+}
\ No newline at end of file
diff --git a/src/cli/CliHandler.ts b/src/cli/CliHandler.ts
new file mode 100644
index 0000000..cafc55f
--- /dev/null
+++ b/src/cli/CliHandler.ts
@@ -0,0 +1,166 @@
+import inquirer, { AllChoiceMap, Answers, AsyncDynamicQuestionProperty, DistinctChoice } from 'inquirer'
+import { Resolution, RESTApiEpisode, RESTApiMediaSource, RESTApiTitle } from '../api/HidiveDao'
+import { ApiKey } from '../models/ApiKey'
+import { User } from '../models/User'
+
+export class CliHandler {
+ public async promptConcurrentDownloads(): Promise {
+ return (await inquirer.prompt({
+ type: 'number',
+ message: 'How many simultaneous downloads would you like to have?',
+ default: 1,
+ name: 'concurrent'
+ })).concurrent
+ }
+
+ public async promptActions(actions: { message: string, value: string }[]): Promise {
+ return this.promptList('What do you want to do?',
+ actions.map(action => {
+ return {
+ name: action.message,
+ value: action.value
+ }
+ })
+ )
+ }
+
+ public async promptEpisodesList(episodes: RESTApiEpisode[]): Promise {
+ return this.promptCheckbox('Which episodes would you like to download?',
+ episodes.map(episode => {
+ return {
+ name: ` E${episode.EpisodeNumberValue} | ${episode.Name}`,
+ value: episode
+ }
+ })
+ )
+ }
+
+ public async promptSeriesList(titles: RESTApiTitle[]): Promise {
+ return this.promptList('Which series would you like to download?',
+ titles.map(series => {
+ return {
+ name: series.Name,
+ value: series
+ }
+ })
+ )
+ }
+
+ public async promptUserList(users: User[]): Promise {
+ return this.promptList('Please select your desired account',
+ users.map(user => {
+ return {
+ name: user.email,
+ value: user
+ }
+ })
+ )
+ }
+
+ public async promptApiKey(apiKeys: ApiKey[]): Promise {
+ return this.promptList('Please select your desired API key',
+ apiKeys.map(apiKey => {
+ return {
+ name: apiKey.key,
+ value: apiKey
+ }
+ })
+ )
+ }
+
+ public async promptGetApiKey(): Promise> {
+ const { key } = await inquirer.prompt([
+ {
+ type: 'input',
+ message: 'Please enter an API key',
+ name: 'key'
+ }
+ ])
+
+ return { key }
+ }
+
+ public async promptGetAuth(): Promise> {
+ const { email, password } = await inquirer.prompt([
+ {
+ type: 'input',
+ message: 'Please enter your E-Mail',
+ name: 'email'
+ },
+ {
+ type: 'input',
+ message: 'Please enter your password',
+ name: 'password'
+ },
+ ])
+
+ return { email, password }
+ }
+
+ public async promptResolutionFromMedia(medias: RESTApiMediaSource[]) {
+ return this.promptList('Which resolution would you like to have? (Unavailable resolutions will fall back to the best)',
+ medias.map(media => {
+ return {
+ name: `${media.RESOLUTION.width}x${media.RESOLUTION.height} (${media.BANDWIDTH / 1000} kB/s)`,
+ value: media.RESOLUTION
+ }
+ })
+ )
+ }
+
+ public async promptVideoLanguage(languages: string[]) {
+ return this.promptList('Which video language would you like to have? (Unavailable languages will fall back to Japanese)',
+ languages.map(language => {
+ return {
+ name: language
+ }
+ })
+ )
+ }
+
+ public async promptList(message: string, choices: AsyncDynamicQuestionProperty>>, Answers> | undefined): Promise {
+ return (await inquirer.prompt({
+ type: 'list',
+ message,
+ name: 'item',
+ choices: choices
+ },
+ { clearPromptOnDone: true })).item
+ }
+
+ public async promptCheckbox(message: string, choices: AsyncDynamicQuestionProperty>>, Answers> | undefined): Promise {
+ return (await inquirer.prompt({
+ type: 'checkbox',
+ message,
+ name: 'item',
+ choices: choices
+ })).item
+ }
+
+ public async promptInput(message: string): Promise {
+ return (await inquirer.prompt({
+ type: 'input',
+ message,
+ name: 'input'
+ })).input
+ }
+
+ public async promptConfirm(message: string): Promise {
+ return (await inquirer.prompt({
+ type: 'list',
+ message,
+ name: 'confirmed',
+ choices: [
+ {
+ name: 'Yes',
+ value: true
+ },
+ {
+ name: 'No',
+ value: false,
+ checked: true
+ }
+ ]
+ })).confirmed
+ }
+}
\ No newline at end of file
diff --git a/src/database/AccountDao.ts b/src/database/AccountDao.ts
new file mode 100644
index 0000000..c236eab
--- /dev/null
+++ b/src/database/AccountDao.ts
@@ -0,0 +1,41 @@
+import { Database } from 'better-sqlite3'
+import { User } from '../models/User'
+
+const createUserStmt = `INSERT INTO Account (email, password, userId, profileId, deviceId, visitId) VALUES (?, ?, ?, ?, ?, ?)`
+const deleteUserStmt = `DELETE FROM Account WHERE id = ?`
+const getUserStmt = `SELECT * FROM Account WHERE id = ?`
+const updateUserStmt = `UPDATE Account SET email = ?, password = ?, userId = ?, profileId = ?, deviceId = ?, visitId = ? WHERE id = ?`
+const getAllStmt = `SELECT * FROM Account`
+
+export class AccountDao {
+ private readonly database: Database
+
+ constructor(database: Database) {
+ this.database = database
+ }
+
+ public deleteAccount(id: number) {
+ const stmt = this.database.prepare(deleteUserStmt)
+ return stmt.run(id)
+ }
+
+ public createAccount(user: Omit) {
+ const stmt = this.database.prepare(createUserStmt)
+ return stmt.run(user.email, user.password, user.userId, user.profileId, user.deviceId, user.visitId)
+ }
+
+ public getAccount(id: number): User | undefined {
+ const stmt = this.database.prepare(getUserStmt)
+ return stmt.get(id)
+ }
+
+ public updateAccount(id: number, user: User) {
+ const stmt = this.database.prepare(updateUserStmt)
+ return stmt.run(user.email, user.password, user.userId, user.profileId, user.deviceId, user.visitId, id)
+ }
+
+ public getAll(): User[] {
+ const stmt = this.database.prepare(getAllStmt)
+ return stmt.all()
+ }
+}
\ No newline at end of file
diff --git a/src/database/ApiKeyDao.ts b/src/database/ApiKeyDao.ts
new file mode 100644
index 0000000..3a30efb
--- /dev/null
+++ b/src/database/ApiKeyDao.ts
@@ -0,0 +1,35 @@
+import { Database } from 'better-sqlite3'
+import { ApiKey } from '../models/ApiKey'
+
+const createApiKeyStmt = `INSERT INTO ApiKey (key) VALUES (?)`
+const deleteApiKeyStmt = `DELETE FROM ApiKey WHERE id = ?`
+const getApiKeyStmt = `SELECT * FROM ApiKey WHERE id = ?`
+const getAllStmt = `SELECT * FROM ApiKey`
+
+export class ApiKeyDao {
+ private readonly database: Database
+
+ constructor(database: Database) {
+ this.database = database
+ }
+
+ public deleteApiKey(id: number) {
+ const stmt = this.database.prepare(deleteApiKeyStmt)
+ return stmt.run(id)
+ }
+
+ public createApiKey(apiKey: Omit) {
+ const stmt = this.database.prepare(createApiKeyStmt)
+ return stmt.run(apiKey.key)
+ }
+
+ public getApiKey(id: number): ApiKey | undefined {
+ const stmt = this.database.prepare(getApiKeyStmt)
+ return stmt.get(id)
+ }
+
+ public getAll(): ApiKey[] {
+ const stmt = this.database.prepare(getAllStmt)
+ return stmt.all()
+ }
+}
\ No newline at end of file
diff --git a/src/database/Database.ts b/src/database/Database.ts
new file mode 100644
index 0000000..578233a
--- /dev/null
+++ b/src/database/Database.ts
@@ -0,0 +1,6 @@
+import path from 'path'
+import Database from 'better-sqlite3'
+
+export function createDatabase() {
+ return new Database(path.resolve('./db/database.db'), { fileMustExist: false })
+}
\ No newline at end of file
diff --git a/src/ffmpeg/FFmpeg.ts b/src/ffmpeg/FFmpeg.ts
new file mode 100644
index 0000000..08ef2c8
--- /dev/null
+++ b/src/ffmpeg/FFmpeg.ts
@@ -0,0 +1,78 @@
+import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
+import path from 'path'
+
+export interface Subtitle {
+ name: string,
+ path: string
+}
+
+export interface Map {
+ map: number
+}
+
+export class FFmpeg {
+ public convertSubtitle(filePath: string, data: Buffer, language: string) {
+ const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'),
+ [
+ '-y',
+ '-i', '-',
+ '-map', '0',
+ '-c', 'srt',
+ '-metadata:s:0', `title="${language}"`,
+ filePath
+ ]
+ )
+ process.stdin.write(data)
+ process.stdin.end()
+ return this.promisifyClose(process, filePath)
+ }
+
+ public convertVideo(filePath: string) {
+ const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'),
+ [
+ '-y',
+ '-i', '-',
+ '-map', '0',
+ '-c', 'copy',
+ filePath
+ ]
+ )
+ return {
+ write: (data: Uint8Array) => process.stdin.write(data),
+ end: () => this.promisifyClose(process, filePath)
+ }
+ }
+
+ public mergeVideoAndSubtitles(filePath: string, videoPath: string, subtitles: Subtitle[]) {
+ let currentMap = 0
+ const getNextMap = () => currentMap = currentMap + 1
+ const subtitlesMaps: (Subtitle & Map)[] = subtitles.map(subtitle => {
+ return {
+ map: getNextMap(),
+ name: subtitle.name,
+ path: subtitle.path
+ }
+ })
+
+ const process = spawn(path.resolve('./ffmpeg/ffmpeg.exe'),
+ [
+ '-y',
+ '-i', videoPath,
+ ...subtitlesMaps.flatMap((subtitle) => ['-i', `${subtitle.path}`]),
+ '-map', '0',
+ ...subtitlesMaps.flatMap((subtitle) => ['-map', `${subtitle.map}`]),
+ '-c', 'copy',
+ ...subtitlesMaps.flatMap((subtitle) => [`-metadata:s:s:${subtitle.map - 1}`, `title=${subtitle.name}`]),
+ filePath
+ ]
+ )
+ return this.promisifyClose(process, filePath)
+ }
+
+ private promisifyClose(process: ChildProcessWithoutNullStreams, value?: T) {
+ return new Promise((res) => {
+ process.stdin.end()
+ return process.on('close', () => res(value as any))
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/file/FileManager.ts b/src/file/FileManager.ts
new file mode 100644
index 0000000..f7d9a2d
--- /dev/null
+++ b/src/file/FileManager.ts
@@ -0,0 +1,40 @@
+import fs from 'fs'
+import path from 'path'
+
+const tempPath = path.resolve('./temp')
+const outputPath = path.resolve('./output')
+
+export class FileManager {
+ public getTempPath(filePath: string) {
+ return path.join(tempPath, filePath)
+ }
+
+ public getOutputPath(filePath: string) {
+ return path.join(outputPath, filePath)
+ }
+
+ public saveFile(filePath: string, data: Buffer) {
+ fs.writeFileSync(filePath, data)
+ return filePath
+ }
+
+ public deleteFile(filePath: string) {
+ fs.unlinkSync(filePath)
+ return filePath
+ }
+
+ public createOutput() {
+ if (!fs.existsSync(outputPath))
+ fs.mkdirSync(outputPath)
+ }
+
+ public createTemp() {
+ if (!fs.existsSync(tempPath))
+ fs.mkdirSync(tempPath)
+ }
+
+ public deleteTemp() {
+ if (fs.existsSync(tempPath))
+ fs.rmSync(tempPath, { recursive: true, force: true })
+ }
+}
\ No newline at end of file
diff --git a/src/hsl/HslManager.ts b/src/hsl/HslManager.ts
new file mode 100644
index 0000000..721331e
--- /dev/null
+++ b/src/hsl/HslManager.ts
@@ -0,0 +1,8 @@
+import crypto from 'crypto'
+
+export class HslManager {
+ public decrypt(data: NodeJS.ArrayBufferView, key: Uint8Array, iv: Uint32Array) {
+ const cipher = crypto.createDecipheriv('aes-128-cbc', key, iv)
+ return cipher.update(data)
+ }
+}
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..5a6c719
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,309 @@
+import { HidiveDao, Resolution } from './api/HidiveDao.js'
+import { CliHandler } from './cli/CliHandler.js'
+import { ApiKeyDao } from './database/ApiKeyDao.js'
+import { createDatabase } from './database/Database.js'
+import { AccountDao } from './database/AccountDao.js'
+import { FFmpeg, Subtitle } from './ffmpeg/FFmpeg.js'
+import { HslManager } from './hsl/HslManager.js'
+import { FileManager } from './file/FileManager.js'
+import async from 'async'
+import cliProgress from 'cli-progress'
+
+const database = createDatabase()
+const accountDao = new AccountDao(database)
+const apiKeyDao = new ApiKeyDao(database)
+const cliHandler = new CliHandler()
+const ffmpeg = new FFmpeg()
+const hslManager = new HslManager()
+const fileManager = new FileManager()
+
+fileManager.createOutput()
+fileManager.deleteTemp()
+
+while (true) {
+ console.clear()
+ const userAction = await cliHandler.promptActions([
+ {
+ message: 'Login',
+ value: 'login'
+ },
+ {
+ message: 'Add API key',
+ value: 'addApiKey'
+ },
+ {
+ message: 'Delete API key',
+ value: 'deleteApiKey'
+ },
+ {
+ message: 'Add account',
+ value: 'addAccount'
+ },
+ {
+ message: 'Delete account',
+ value: 'deleteAccount'
+ }
+ ])
+
+ switch (userAction) {
+ case 'login': {
+ await hidiveAction()
+ break
+ }
+ case 'addApiKey': {
+ await addApiKey()
+ break
+ }
+ case 'deleteApiKey': {
+ await deleteApiKey()
+ break
+ }
+ case 'addAccount': {
+ await addAccount()
+ break
+ }
+ case 'deleteAccount': {
+ await deleteAccount()
+ break
+ }
+ }
+}
+
+
+async function hidiveAction() {
+ const hidiveDao = await login()
+ try {
+ const pingRes = await hidiveDao.ping()
+ const initDeviceRes = await hidiveDao.initDevice()
+ const initVisitRes = await hidiveDao.initVisit()
+ const authenticateRes = await hidiveDao.authenticate()
+
+ if (!hidiveDao.user || !hidiveDao.profile)
+ throw new Error(`Failed to authenticate response returned code ${authenticateRes.Code} with status ${authenticateRes.Status}`)
+
+ accountDao.updateAccount(hidiveDao.options.user.id, {
+ ...hidiveDao.options.user,
+ deviceId: initDeviceRes.Data.DeviceId,
+ userId: hidiveDao.user.Id,
+ profileId: hidiveDao.profile.Id,
+ visitId: initVisitRes.Data.VisitId
+ })
+
+ console.log(`Successfully logged into ${hidiveDao.profile.Nickname}`)
+ } catch (error) {
+ console.error(error)
+ return
+ }
+
+ while (true) {
+ console.clear()
+ const hidiveAction = await cliHandler.promptActions([
+ {
+ message: 'Download a series',
+ value: 'download'
+ },
+ {
+ message: 'Return',
+ value: 'return'
+ }
+ ])
+
+ switch (hidiveAction) {
+ case 'download': {
+ await downloadSeries(hidiveDao)
+ break
+ }
+ case 'return': {
+ return
+ }
+ }
+ }
+}
+
+async function login() {
+ console.clear()
+ let users = accountDao.getAll()
+ let apiKeys = apiKeyDao.getAll()
+
+ if (apiKeys.length <= 0) {
+ const apiKey = await cliHandler.promptGetApiKey()
+ apiKeyDao.createApiKey(apiKey)
+ apiKeys = apiKeyDao.getAll()
+ }
+
+ if (users.length <= 0) {
+ const user = await cliHandler.promptGetAuth()
+ accountDao.createAccount(user)
+ users = accountDao.getAll()
+ }
+
+ const user = await cliHandler.promptUserList(users)
+ const apiKey = await cliHandler.promptApiKey(apiKeys)
+
+ const hidiveDao = new HidiveDao({
+ apiKey: apiKey,
+ user: user
+ })
+
+ return hidiveDao
+}
+
+async function deleteApiKey() {
+ const apiKey = await cliHandler.promptApiKey(apiKeyDao.getAll())
+ const confirmed = await cliHandler.promptConfirm('Do you really want to delete this API key?')
+ if (confirmed)
+ apiKeyDao.deleteApiKey(apiKey.id)
+}
+
+async function addApiKey() {
+ const apiKey = await cliHandler.promptGetApiKey()
+ apiKeyDao.createApiKey(apiKey)
+}
+
+async function deleteAccount() {
+ const account = await cliHandler.promptUserList(accountDao.getAll())
+ const confirmed = await cliHandler.promptConfirm('Do you really want to delete this account?')
+ if (confirmed)
+ accountDao.deleteAccount(account.id)
+}
+
+async function addAccount() {
+ const account = await cliHandler.promptGetAuth()
+ accountDao.createAccount(account)
+}
+
+async function downloadSeries(hidiveDao: HidiveDao) {
+ console.clear()
+ const searchQuery = await cliHandler.promptInput('What\'s your series called?')
+ const searchResultsRes = await hidiveDao.search(searchQuery)
+
+ if (searchResultsRes.Data.TitleResults.length <= 0) {
+ console.log(`Oh no! No series named ${searchQuery} or similar was found.`)
+
+ const retry = await cliHandler.promptConfirm('Do you want to search again?')
+ if (retry)
+ await downloadSeries(hidiveDao)
+ return
+ }
+
+ const selectedSeries = await cliHandler.promptSeriesList(searchResultsRes.Data.TitleResults)
+ const getTitleRes = await hidiveDao.getTitle(selectedSeries.Id)
+ const checkedEpisodes = await cliHandler.promptEpisodesList(getTitleRes.Data.Title.Episodes)
+ const concurrentDownloads = await cliHandler.promptConcurrentDownloads()
+ if (checkedEpisodes.length <= 0)
+ return
+
+ let standardVideoLanguage: string | undefined
+ let standardMediaResolution: Resolution | undefined
+ let resolveVideoLanguage: (language: string) => void
+ let resolveMediaResolution: (resolution: Resolution) => void
+ const videoLanguagePromise = new Promise(res => resolveVideoLanguage = (language) => {
+ standardVideoLanguage = language,
+ res(undefined)
+ })
+ const mediaResolutionPromise = new Promise(res => resolveMediaResolution = (resolution) => {
+ standardMediaResolution = resolution,
+ res(undefined)
+ })
+
+ const keys: { [key: string]: Uint8Array } = {}
+
+ const multiBar = new cliProgress.MultiBar({
+ clearOnComplete: true,
+ format: '{bar} | {value}/{total} | {download} Mb/s | {episode}'
+ }, cliProgress.Presets.rect)
+
+ await async.eachLimit(checkedEpisodes, concurrentDownloads, async (episode) => {
+ fileManager.createTemp()
+ const getVideoRes = await hidiveDao.getVideos(episode.TitleId, episode.VideoKey)
+
+ const { Data } = getVideoRes
+ const { VideoLanguages, CaptionLanguages, VideoUrls, CaptionVttUrls, FontSize, VideoLanguage } = Data
+
+ if (!standardVideoLanguage && checkedEpisodes.indexOf(episode) === 0) {
+ resolveVideoLanguage(await cliHandler.promptVideoLanguage(VideoLanguages))
+ }
+ await videoLanguagePromise
+
+ let videoLanguage = standardVideoLanguage
+ let videoUrl = VideoUrls[standardVideoLanguage || '']
+ if (!videoUrl) {
+ videoUrl = VideoUrls[VideoLanguage]
+ videoLanguage = VideoLanguage
+ }
+
+ const mediaSourcesRes = await hidiveDao.getMediaSources(videoUrl.hls[0])
+
+ if (!standardMediaResolution && checkedEpisodes.indexOf(episode) === 0) {
+ resolveMediaResolution(await cliHandler.promptResolutionFromMedia(mediaSourcesRes.playlists.map(playlist => playlist.attributes)))
+ console.clear()
+ }
+ await mediaResolutionPromise
+
+ let videoResolution = standardMediaResolution
+ let playlist = mediaSourcesRes.playlists.find(playlist => playlist.attributes.RESOLUTION === standardMediaResolution)
+ if (!playlist) {
+ videoResolution = mediaSourcesRes.playlists[0].attributes.RESOLUTION
+ playlist = mediaSourcesRes.playlists[0]
+ }
+
+ const episodeFileName = Array.from(`[E${`${episode.EpisodeNumberValue}`.padStart(2, '0')}S${`${episode.SeasonNumber}`.padStart(2, '0')}] ${selectedSeries.Name} - ${episode.Name} [${videoResolution?.height}p] [${videoLanguage}]`.matchAll(/[^/./\\:*?\"<>|]/g)).join('')
+ const mediasRes = await hidiveDao.getMedia(playlist.uri)
+ const tempEpisodePath = fileManager.getTempPath(`${episodeFileName}.mkv`)
+ const episodePath = fileManager.getOutputPath(`${episodeFileName}.mkv`)
+
+ const ffmpegConvertVideoProcess = ffmpeg.convertVideo(tempEpisodePath)
+
+ const bar = multiBar.create(mediasRes.segments.length, 0, { download: 0, episode: `${selectedSeries.Name} - ${episode.Name}` })
+
+ const startTime = Date.now()
+ let bytesDownloaded = 0
+ let segmentsDownloaded = 0
+
+ for (const segment of mediasRes.segments) {
+ let segmentData = await hidiveDao.getTs(segment.uri)
+
+ if (segment.key) {
+ const { uri, iv } = segment.key
+
+ let key = keys[uri]
+ if (!key)
+ key = keys[uri] = await hidiveDao.getKey(uri)
+
+ segmentData = hslManager.decrypt(segmentData, key, iv)
+ }
+
+ bytesDownloaded += segmentData.byteLength
+ segmentsDownloaded++
+ ffmpegConvertVideoProcess.write(segmentData)
+ bar.update(segmentsDownloaded, { download: ((bytesDownloaded / 1000000) / ((Date.now() - startTime) / 1000)).toFixed(2), episode: `${selectedSeries.Name} - ${episode.Name}` })
+ }
+ await ffmpegConvertVideoProcess.end()
+
+ const subtitles: {
+ name: string,
+ value: Buffer
+ }[] = await Promise.all(Object.entries(CaptionVttUrls)
+ .map(async ([name, url]) => {
+ return {
+ name,
+ value: Buffer.from(await hidiveDao.getSub(url))
+ }
+ }))
+
+ const subtitlePaths: Subtitle[] = await Promise.all(subtitles
+ .map(async (subtitle) => {
+ return {
+ name: subtitle.name,
+ path: await ffmpeg
+ .convertSubtitle(fileManager
+ .getTempPath(`${episodeFileName}[${subtitle.name}].srt`), subtitle.value, subtitle.name)
+ }
+ }))
+
+ await ffmpeg.mergeVideoAndSubtitles(episodePath, tempEpisodePath, subtitlePaths)
+ })
+ fileManager.deleteTemp()
+}
+
+export { }
\ No newline at end of file
diff --git a/src/models/ApiKey.ts b/src/models/ApiKey.ts
new file mode 100644
index 0000000..35b1c09
--- /dev/null
+++ b/src/models/ApiKey.ts
@@ -0,0 +1,4 @@
+export interface ApiKey {
+ id: number,
+ key: string,
+}
\ No newline at end of file
diff --git a/src/models/User.ts b/src/models/User.ts
new file mode 100644
index 0000000..e83127a
--- /dev/null
+++ b/src/models/User.ts
@@ -0,0 +1,9 @@
+export interface User {
+ id: number,
+ email: string,
+ password: string,
+ userId?: number,
+ profileId?: number,
+ deviceId?: string,
+ visitId?: string
+}
\ No newline at end of file
diff --git a/src/util/utils.ts b/src/util/utils.ts
new file mode 100644
index 0000000..3b5cc28
--- /dev/null
+++ b/src/util/utils.ts
@@ -0,0 +1 @@
+export const sleep = (ms: number) => new Promise((res) => setTimeout(() => res, ms))
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..f8ec739
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,105 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ "module": "ES2022", /* Specify what module code is generated. */
+ "rootDir": "./src", /* Specify the root folder within your source files. */
+ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./dist", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules"]
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..7e88ed5
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,890 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/runtime@^7.12.5":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
+ integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@tsconfig/node10@^1.0.7":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
+ integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
+ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
+
+"@types/async@^3.2.15":
+ version "3.2.15"
+ resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.15.tgz#26d4768fdda0e466f18d6c9918ca28cc89a4e1fe"
+ integrity sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g==
+
+"@types/better-sqlite3@^7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.0.tgz#603d1c7a72527dd946e2bf641ed4c0b74a547423"
+ integrity sha512-rnSP9vY+fVsF3iJja5yRGBJV63PNBiezJlYrCkqUmQWFoB16cxAHwOkjsAYEu317miOfKaJpa65cbp0P4XJ/jw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/cli-progress@^3.11.0":
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.0.tgz#ec79df99b26757c3d1c7170af8422e0fc95eef7e"
+ integrity sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg==
+ dependencies:
+ "@types/node" "*"
+
+"@types/ffmpeg@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@types/ffmpeg/-/ffmpeg-1.0.4.tgz#e82c080f7846de13e17bf0a2d49dd841651b8686"
+ integrity sha512-USbiJIZRuoO1n/HUTkxCPH0hvjVUOLUbkicwbh+9GOjwg0IFZOU0aDVMJV0A27nC42Bx6rujunU6pfdjw866JQ==
+
+"@types/fs-extra@^9.0.13":
+ version "9.0.13"
+ resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45"
+ integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/inquirer@^9.0.2":
+ version "9.0.2"
+ resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-9.0.2.tgz#846b141c17608aad9fc62572280753bb6ec09184"
+ integrity sha512-MQc3adiIh/rDxLkjnvL03rSvuHg+/dKif4dn/MRsnE+oU1bAdyuDbW0w+ewR1M/M/u/Z0YAbw7NZYCpgQ5SW8A==
+ dependencies:
+ "@types/through" "*"
+ rxjs "^7.2.0"
+
+"@types/node@*", "@types/node@^18.8.0":
+ version "18.8.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.0.tgz#b8ee8d83a99470c0661bd899417fcd77060682fe"
+ integrity sha512-u+h43R6U8xXDt2vzUaVP3VwjjLyOJk6uEciZS8OSyziUQGOwmk+l+4drxcsDboHXwyTaqS1INebghmWMRxq3LA==
+
+"@types/through@*":
+ version "0.0.30"
+ resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
+ integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==
+ dependencies:
+ "@types/node" "*"
+
+"@videojs/vhs-utils@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz#665ba70d78258ba1ab977364e2fe9f4d4799c46c"
+ integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ global "^4.4.0"
+ url-toolkit "^2.2.1"
+
+acorn-walk@^8.1.1:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+ integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+
+acorn@^8.4.1:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
+ integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+
+ansi-escapes@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6"
+ integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==
+ dependencies:
+ type-fest "^1.0.2"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
+ integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
+
+ansi-styles@^6.1.0:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.1.tgz#63cd61c72283a71cb30bd881dbb60adada74bc70"
+ integrity sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==
+
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+async@^3.2.4:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+ integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^0.27.2:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
+ integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+ dependencies:
+ follow-redirects "^1.14.9"
+ form-data "^4.0.0"
+
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+better-sqlite3@^7.6.2:
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.6.2.tgz#47cd8cad5b9573cace535f950ac321166bc31384"
+ integrity sha512-S5zIU1Hink2AH4xPsN0W43T1/AJ5jrPh7Oy07ocuW/AKYYY02GWzz9NH0nbSMn/gw6fDZ5jZ1QsHt1BXAwJ6Lg==
+ dependencies:
+ bindings "^1.5.0"
+ prebuild-install "^7.1.0"
+
+bindings@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+ integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+ dependencies:
+ file-uri-to-path "1.0.0"
+
+bl@^4.0.3:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+ integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ dependencies:
+ buffer "^5.5.0"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
+bl@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2"
+ integrity sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==
+ dependencies:
+ buffer "^6.0.3"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
+buffer@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
+buffer@^6.0.3:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
+ integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.2.1"
+
+chalk@^5.0.0, chalk@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6"
+ integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==
+
+chardet@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+ integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+chownr@^1.1.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
+ integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+
+cli-cursor@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea"
+ integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==
+ dependencies:
+ restore-cursor "^4.0.0"
+
+cli-progress@^3.11.2:
+ version "3.11.2"
+ resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77"
+ integrity sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA==
+ dependencies:
+ string-width "^4.2.3"
+
+cli-spinners@^2.6.1:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a"
+ integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==
+
+cli-width@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6"
+ integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==
+
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+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==
+
+decompress-response@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
+ integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
+ dependencies:
+ mimic-response "^3.1.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==
+
+defaults@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+ integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==
+ dependencies:
+ clone "^1.0.2"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+detect-libc@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
+ integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
+
+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-walk@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
+ integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
+
+eastasianwidth@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+ integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
+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==
+
+emoji-regex@^9.2.2:
+ version "9.2.2"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+ integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
+end-of-stream@^1.1.0, end-of-stream@^1.4.1:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+ integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+ dependencies:
+ once "^1.4.0"
+
+escape-string-regexp@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
+ integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
+
+expand-template@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
+ integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+
+external-editor@^3.0.3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+ integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+ dependencies:
+ chardet "^0.7.0"
+ iconv-lite "^0.4.24"
+ tmp "^0.0.33"
+
+figures@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f"
+ integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==
+ dependencies:
+ escape-string-regexp "^5.0.0"
+ is-unicode-supported "^1.2.0"
+
+file-uri-to-path@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+ integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+follow-redirects@^1.14.9:
+ version "1.15.2"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
+ integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
+fs-constants@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+ integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
+fs-extra@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+ integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+github-from-package@0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+ integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
+
+global@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
+ integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
+ dependencies:
+ min-document "^2.19.0"
+ process "^0.11.10"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+iconv-lite@^0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+ieee754@^1.1.13, 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==
+
+inherits@^2.0.3, inherits@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+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==
+
+inquirer@^9.1.2:
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.1.2.tgz#37f5486f3de0e38820aad83a1f75c52c747e2f9a"
+ integrity sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg==
+ dependencies:
+ ansi-escapes "^5.0.0"
+ chalk "^5.0.1"
+ cli-cursor "^4.0.0"
+ cli-width "^4.0.0"
+ external-editor "^3.0.3"
+ figures "^5.0.0"
+ lodash "^4.17.21"
+ mute-stream "0.0.8"
+ ora "^6.1.2"
+ run-async "^2.4.0"
+ rxjs "^7.5.6"
+ string-width "^5.1.2"
+ strip-ansi "^7.0.1"
+ through "^2.3.6"
+ wrap-ansi "^8.0.1"
+
+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-interactive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90"
+ integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
+
+is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714"
+ integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==
+
+jsonfile@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93"
+ integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==
+ dependencies:
+ chalk "^5.0.0"
+ is-unicode-supported "^1.1.0"
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+m3u8-parser@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-6.0.0.tgz#e9143313b44f07bb25fdea1c8aac1098d9ada192"
+ integrity sha512-s3JfDtqhxTilZQf+P1m9dZc4ohL4O/aylP1VV6g9lhKuQNfAcVUzq7d2wgJ9nZR4ibjuXaP87QzGCV6vB0kV6g==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ "@videojs/vhs-utils" "^3.0.5"
+ global "^4.4.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==
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mimic-response@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
+ integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
+
+min-document@^2.19.0:
+ version "2.19.0"
+ resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+ integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==
+ dependencies:
+ dom-walk "^0.1.0"
+
+minimist@^1.2.0, minimist@^1.2.3:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+ integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
+mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
+ integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
+
+mute-stream@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+ integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
+napi-build-utils@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
+ integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
+
+node-abi@^3.3.0:
+ version "3.25.0"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.25.0.tgz#ca57dd23ae67679ce152b6c45cae2c57ed04faff"
+ integrity sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ==
+ dependencies:
+ semver "^7.3.5"
+
+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 sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+ora@^6.1.2:
+ version "6.1.2"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.2.tgz#7b3c1356b42fd90fb1dad043d5dbe649388a0bf5"
+ integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==
+ dependencies:
+ bl "^5.0.0"
+ chalk "^5.0.0"
+ cli-cursor "^4.0.0"
+ cli-spinners "^2.6.1"
+ is-interactive "^2.0.0"
+ is-unicode-supported "^1.1.0"
+ log-symbols "^5.1.0"
+ strip-ansi "^7.0.1"
+ wcwidth "^1.0.1"
+
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+
+prebuild-install@^7.1.0:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
+ integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==
+ dependencies:
+ detect-libc "^2.0.0"
+ expand-template "^2.0.3"
+ github-from-package "0.0.0"
+ minimist "^1.2.3"
+ mkdirp-classic "^0.5.3"
+ napi-build-utils "^1.0.1"
+ node-abi "^3.3.0"
+ pump "^3.0.0"
+ rc "^1.2.7"
+ simple-get "^4.0.0"
+ tar-fs "^2.0.0"
+ tunnel-agent "^0.6.0"
+
+process@^0.11.10:
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+ integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
+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"
+
+rc@^1.2.7:
+ 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@^3.1.1, readable-stream@^3.4.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+regenerator-runtime@^0.13.4:
+ version "0.13.9"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+ integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
+restore-cursor@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9"
+ integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
+run-async@^2.4.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+ integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
+
+rxjs@^7.2.0, rxjs@^7.5.6:
+ version "7.5.7"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39"
+ integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==
+ dependencies:
+ tslib "^2.1.0"
+
+safe-buffer@^5.0.1, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^7.3.5:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+ dependencies:
+ lru-cache "^6.0.0"
+
+signal-exit@^3.0.2:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+simple-concat@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
+ integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
+
+simple-get@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
+ integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
+ dependencies:
+ decompress-response "^6.0.0"
+ once "^1.3.1"
+ simple-concat "^1.0.0"
+
+string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+ integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+ dependencies:
+ eastasianwidth "^0.2.0"
+ emoji-regex "^9.2.2"
+ strip-ansi "^7.0.1"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
+ integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
+ dependencies:
+ ansi-regex "^6.0.1"
+
+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 sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
+
+tar-fs@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
+ integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
+ dependencies:
+ chownr "^1.1.1"
+ mkdirp-classic "^0.5.2"
+ pump "^3.0.0"
+ tar-stream "^2.1.4"
+
+tar-stream@^2.1.4:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+ integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+ dependencies:
+ bl "^4.0.3"
+ end-of-stream "^1.4.1"
+ fs-constants "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.1.1"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tmp@^0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+ts-node@^10.9.1:
+ version "10.9.1"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
+ integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
+tslib@^2.1.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+ dependencies:
+ safe-buffer "^5.0.1"
+
+type-fest@^1.0.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
+ integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
+
+typescript@^4.8.4:
+ version "4.8.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
+ integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
+
+universalify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
+ integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
+
+url-toolkit@^2.2.1:
+ version "2.2.5"
+ resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.2.5.tgz#58406b18e12c58803e14624df5e374f638b0f607"
+ integrity sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==
+
+util-deprecate@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+wcwidth@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==
+ dependencies:
+ defaults "^1.0.3"
+
+wrap-ansi@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3"
+ integrity sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==
+ dependencies:
+ ansi-styles "^6.1.0"
+ string-width "^5.0.1"
+ strip-ansi "^7.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==